When should I use ConstantDataArray versus ConstantArray? - llvm

I'm using the LLVM 3.3 C++-API. My code creates arrays using ConstantArray::get, and multidimensional arrays with recursive code (the innermost rank is first converted to a vector of Constant*s, as above, then that is used to initialize the next-innermost rank, and so on).
I tried to optimize this by saying that if an array's element type satisfies the ConstantDataArray::isElementTypeCompatible predicate, then it should be initialized using ConstantDataArray::get.
Here's a more concrete example to show what I mean:
Say the array I want to create would be declared like this in C++:
int x[2][3] = {{1,2,3},{4,5,6}};
The innermost rank (type int[3]) is a simple array type, so two CDAs are created.
The next rank is an array of two int[3]s. It is not a simple type, so a regular ConstantArray is created. The argument is an ArrayRef<Constant*> containing the two CDAs.
At step 3, ConstantArray complains because the initializer does not have exactly the right type. Here's the message:
.../llvm-3.3.src/lib/IR/Constants.cpp:754:
static llvm::Constant* llvm::ConstantArray::get(llvm::ArrayType*,
llvm::ArrayRef<llvm::Constant*>):
Assertion `V[i]->getType() == Ty->getElementType()
&& "Wrong type in array element initializer"' failed.
I though ConstantDataArray was a substitute for ConstantArray when the element type is simple enough, but maybe I have it wrong. What's the correct way to understand it?
Update
This looks like a bug in my code (outside of LLVM). ConstantDataArray does appear to be a transparent substitute for ConstantArray.
Here's the code I put together to demonstrate the problem. It actually runs through without any complaints from LLVM:
// int[2][3] = {{1,2,3},{4,5,6}};
IntegerType* i64 = IntegerType::get(mod->getContext(), 64);
Constant* one = ConstantInt::get(i64, 1);
Constant* two = ConstantInt::get(i64, 2);
Constant* three = ConstantInt::get(i64, 3);
Constant* four = ConstantInt::get(i64, 4);
Constant* five = ConstantInt::get(i64, 5);
Constant* six = ConstantInt::get(i64, 6);
ArrayType* int_3 = ArrayType::get(i64, 3);
ArrayType* int_2_3 = ArrayType::get(int_3, 2);
// Constant* array123 = ConstantArray::get(int_3, std::vector<Constant*>{one,two,three});
Constant* array123 = ConstantDataArray::get(mod->getContext(), std::vector<uint64_t>{1,2,3});
// Constant* array456 = ConstantArray::get(int_3, std::vector<Constant*>{four,five,six});
Constant* array456 = ConstantDataArray::get(mod->getContext(), std::vector<uint64_t>{4,5,6});
Constant* array = ConstantArray::get(int_2_3, std::vector<Constant*>{array123, array456});
In case anyone is interested, the assertion stems from my reversing the array extents. int[2][3] is an array of two arrays of three. I'm overloading operator[] to build an array type as i64[2][3], where i64 is an object that holds an IntegerType* and overloads operator[]. The problem is that this builds an array of three arrays of two.

Related

Is there a literal for the empty array in Chapel?

I'm trying to make an empty array in Chapel. An array of one element can be made like this:
var a: [1..1] int = (1);
But when I try
var b: [1..0] int = ();
I get
syntax error: near ')'
Is there an empty array literal in Chapel? I haven't been able to find an example.
EDIT
The reason I am trying to get an empty array is that I would like to implement get this function working for empty arrays:
proc sum_of_even_squares(a) {
// Does this work for empty arrays? Probably not.
return + reduce ([x in a] if x % 2 == 0 then x*x else 0);
}
assert(sum_of_even_squares([7]) == 0);
assert(sum_of_even_squares([7, 3]) == 0);
assert(sum_of_even_squares([7, 3, -8]) == 64);
assert(sum_of_even_squares([7, 3, -8, 4]) == 80);
But I am unable to form an empty array literal.
Generally, in Chapel, to declare an empty thing, you specify its type but no initialization, such as
var i:int;
But to declare an integer initialized with a value, you'd probably leave off the type:
var j = 2;
In this case, simply omitting the initializer makes it work.
var b: [1..0] int;
Relatedly, (1) is not declaring an array literal but rather a tuple literal. The syntax [1] would declare an array literal. At present, zero-length tuples are not supported in the compiler implementation. It might be easier to get zero-length array literals to work, but it doesn't seem to work right now either (in 1.15).
And how would a zero-length array literal know the type of the elements? For this reason I don't think it could help in your specific case.

Error when attempting to change value of float?

Trying to change the value of a certain float which is used to define the RGBA of an element. The problem is when I try to change the value of the float an error occurs. Here's an example:
float ColorForScrollbar[4] = {1,0,0,.8};
// Set the value for ColorForScrollbar
ColorForScrollbar[4] = {0,1,0,.8};
// "Error: Expected an expression"
ColorForScrollbar = {0,1,0,.8};
// "Error: Expression must be a modifiable value"
float ColorForScrollbar[4] = {1,0,0,.8};
This is valid. The {1,0,0,.8} is an initializer. It is not, however, an expression. It can only be used after the = in an object declaration.
ColorForScrollbar[4] = {0,1,0,.8};
If this were legal, then it would (attempt to) change the value of ColorForScrollbar[4], not of the entire array. Since the only elements that exist have indices 0, 1, 2, and 3, this would have undefined behavior. But again, {0,1,0,.8} is not an expression, so it can't be used on the RHS of an assignment.
This is closer, but it has the same problem as before. Furthermore, there is no assignment operator for array types.
You can change one element at a time:
ColorForScrollbar[0] = 0;
ColorForScrollbar[1] = 1;
ColorForScrollbar[2] = 0;
ColorForScrollbar[3] = 0.8;
Or, if you want to use the initializer syntax, you can use a temporary object:
#include <cstring>
const float new_value[4] = { 0, 1, 0, 0.8 };
std::memcpy((void*)ColorForScrollbar, (void*)new_value), sizeof ColorForScrollbar);
However, this is all rather low-level. You're probably better off using one of the C++ container classes from the standard library. Which one is best (std::vector, std::array) probably depends on just what you're doing.
An array is really just an address for a contiguous, fixed, area of memory. You can't change it any more than you can change your own street address (unless you move, of course).
What you can change, though, are the contents of an array:
ColorForScrollbar[0]=0;
ColorForScrollbar[1]=1;
ColorForScrollbar[2]=0;
ColorForScrollbar[3]=.8;
You don't have to change every value in the array, just what you need to change.
You can also do an explicit copy:
float NewColorForScrollBar[4] = {0,1,0,.8};
for (i=0; i<4; ++i)
ColorForScrollbar[i]=NewColorForScrollbar[i];

Create global vector using LLVM IR Builder

I want to build up LLVM IR for the following expression to add a scalar to a vector
[1,2,3,4]+1
I have found the correct methods to create the add and the scalar expression but not for the vector.
Value *L = //Missing code here
Value *R = ConstantFP::get(getGlobalContext(), APFloat(Val));
Value *exp = Builder.CreateFAdd(L, R, "addresult");
How can I generate this vector?
First make sure you actually need a vector i.e. a datatype on which you can operate in parallel (SIMD/SIMT fashion) and not a simple array.
After that make also sure the type you intend to use is right (APFloat is arbitrary precision float).
Creating a vector can proceed in the same way you add elements via insertelement
Type* u32Ty = Type::getInt32Ty( llvmContext );
Type* vecTy = VectorType::get(u32Ty, 4);
Value* emptyVec = UndefValue::get(vecTy);
Constant* index0 = Constant::getIntegerValue(u32Ty, llvm::APInt(32, 0));
Value* insert1 = InsertElementInst(/*First integer value*/, emptyVec, index0, 0);

C++ and recursion with arrays

I've programmed with other languages, but now that I am learning C++, I've found a problem. I am trying to solve a problem with recursion, with a method that takes an array as an argument. I thought about using a public array, maybe, but I can't use the array either way.
From what I've read, it seems to me that it has something to do with the memory. (I thought that, even though it consumes a lot of memory, creating it again with each call would work.)
Here's some code:
static void FindSolution(int row, int column, bool answer[][8][8]) {
for(int i = 0; i < 8; i++)
//Some processing…
bool temp = true;
FindSolution(0, column + 1, answer[row][column] = temp);
}
}
How do I get to actually use the array? Somehow.
The error:
error: array type 'bool [8]' is not assignable
FindSolution(0, column + 1, answer[row][column] = temp);
You have an extra [] on your array. You've declared it as a 3D array, but then you try to assign to it like it is a 2D array. The compiler gets upset because you try to assign a bool value to an array, which is exactly what you are doing:
answer[row][column] = temp;
temp has type bool, but answer[row][column] has type bool[8].
Instead declare the argument without the extra []:
static void FindSolution(int row, int column, bool answer[8][8]) {
You keep incrementing 'column', but you never check it to make sure it doesn't reach 8. When it does reach 8, you're off the end of the array, and you get an error.
There are a few immediate problems with this.
First Problem: Function signature is incorrect
You've declared the third parameter as a 3-dimensional array, but you only want to deal with two dimensions it seems. There are a couple of ways you can redeclare this function to accept a 2D array, for all the options see the accepted answer here. Personally, in this situation I'd go with a template option unless there is a specific reason not to. Something like the following:
template<size_t _rows, size_t _columns>
static void FindSolution(int row, int column, bool (&answer)[_rows][_columns]) {
// todo: Some processing...
}
This allows you to accurately know the size of the array at compile time, of course this won't work so well with dynamically allocated arrays but seeing as you seemed to know the dimensions of the array already at compile time, I figured this wasn't an issue. If it is, check the other ways of passing a 2D array to a function in the link I attached.
Second issue: Recursive call
The second issue is how you're doing your recursive call.
FindSolution(0, column + 1, answer[row][column] = temp);
The result of the assignation of temp to the specific location in the answer array is not the answer array, but rather the value of temp. Effectively the following statement:
answer[row][column] = temp
Is trying to pass a single bool value as a 2-dimensional array, which won't work. In order to correctly call the method again you'll need to do your assignation of temp to the answer array, then call the function again.
answer[row][column] = temp;
FindSolution<_rows,_columns>(0, column + 1, answer);
Should work fine. (Note the explicit template arguments here <_rows,_columns>, this is only needed if you're using the function signature I posted above which made use of templates)

G++ gives cryptic error messages, some pointer issue

I'm new to C++ and struggling trying to make multi-file programs work. I had a Graph library functioning in C, and I am running into trouble converting it to C++. My biggest problem from g++ is this error message,
error: no match for âoperator=â in â*(((Graph*)this)->Graph::adj +
((long unsigned int)(((long unsigned int)i) * 32ul))) = (operator
new(32u), (<statement>, ((List*)<anonymous>)))â
Here is the section of my Graph.cpp code it is complaining about:
Graph::Graph(int n){
order = n;
size = 0;
source = NIL;
color = static_cast<char*>(calloc(n + 1, sizeof(char)));
distance = static_cast<int*>(calloc(n + 1, sizeof(int)));
parent = static_cast<int*>(calloc(n + 1, sizeof(int)));
adj = static_cast<List*>(calloc(n + 1, sizeof(List*)));
discover = static_cast<int*>(calloc(n + 1, sizeof(int)));
finish = static_cast<int*>(calloc(n + 1, sizeof(int)));
int i;
for(i = 0; i <= n; i++){
color[i] = 'w';
distance[i] = INF;
parent[i] = NIL;
adj[i] = new List();
}
}
As you can see I'm using a bit of a hybrid between C and C++, but a pure C++ implementation doesn't work either. Further down in my file, I constantly get the error " error: base operand of â->â has non-pointer type âListâ", and I think it all comes from how I'm declaring. I've looked around on here and have found other people complaining of this error, but I haven't seen it help with assigning to an array. Help would be awesome, because except for this I know it all works.
You have declared Graph::adj to be of type List* (adj[i] is the same as doing *(adj+i)), and dereferencing a pointer to type T will yield a value of type T, ie. the type of adj[i] is List.
If you really want to do what you are trying to do declare Graph::adj as List**, that way it will be a pointer to a bunch of pointers to List, not a pointer to a bunch of Lists.
I don't understand, what the heck are you trying to say?
You are trying to assign a List* (returned by new List) to a variable of type List.
struct List {
// ...
};
List ** adj = static_cast<List**> (
std::calloc (10, sizeof (List*))
);
/* error
List * adj = static_cast<List*> (
std::calloc (10, sizeof (List*))
);
*/
adj[0] = new List;
Okay, I get you.. but there must be a better way to solve this issue?
There sure is, use what c++ provides you with and ditch your old C habits.
List * adj = new List [10]; // create an array of 10 List
adj[i].member_of_List ...; // example
delete [] adj; // free up allocated memory when you are done
Line
adj = static_cast<List*>(calloc(n + 1, sizeof(List*)));
suggests that adj is an array of List objects, while line
adj[i] = new List();
suggests that it is an array of pointers. You should check the definition of adj. By the way, the error refers to the latter line.
Types
I'll elaborate a little more on the theoretical aspects of the problem, which revolves around the C++ type system.
In C++ every expression has a type, known at compile time. The type of any expression of the form
static_cast<List*>(...)
is
List *
That is, provided that the type of the expression in parenthesis can be statically casted to List *, the compiler will not complain and the final type of the expression will be List *. Function calloc returns void * which can be casted to any other pointer; in fact, pointer casts are often only a way to tell the compiler yes, I know what I'm doing, shut up. On most platforms all pointer types have the same bit representation, although the standard does not mandate such a thing, hence in principle no machine code needs to be generated to implement such cast.
The type of an expression such as
new List()
or
new List[10]
is
List *
and an instruction that includes such instructions allocates enough space for a List object or for 10 list objects and returns a pointer to such space.
You would probably be better off if you replace the line
adj = static_cast<List*>(calloc(n + 1, sizeof(List*)));
with
adj = new List[n + 1];
If adj is a pointer type, the type of an expression such as
adj[i]
or
*(adj + i)
or even simply
*adj
is the pointer type minus one of its asterisks; that is, if the type of adj is
List *
the type of adj[i] is
List
That is the reason why you get the error at the line adj[i] = new List(); it doesn't make sense to assign
List *
to
List
The good news, is that if you allocate adj with the new operator, you probably don't need to create with new every single element; when you allocate an array with new, all its elements are created and their constructor is automatically invoked by the compiler.
Possible solution
Since the rest of your program is aparently correct, you may simply erase the line
adj[i] = new List()
and everything should work fine. However as john noted, your intent is not very clear; you really need to tell us if you want adj to be an array of objects
List *adj;
or an array of pointers
List **adj;
Advanced topic
The practical difference between using the C way
adj = static_cast<List*>(calloc(n + 1, sizeof(List*)));
or the C++ way
adj = new List[n + 1];
Is that the constructor of each of the allocated List objects doesn't get called with the former method. There are a few applications of pointers in which you may want this to be the case, but in general you shouldn't mix C and C++.
A hybrid mix of C and C++ is actually going to be harder than pure C++.
In this case the problem seems to be that you have
class Graph
{
...
List* adj;
...
};
so
adj[i] = new List();
is wrong because adj[i] is of type List and new List() is of type List*.
The fix for this is to use placement new.
#include <new>
new (adj + i) List();
This will construct a List at address &adj[i] which is what you are trying to achieve I think.
But this is advanced C++ and is only necessary because you are trying to write a mix of C and C++. I would ditch the C part as soon as possible.
EDIT
OK as refp points out (and I missed) you have
adj = static_cast<List*>(calloc(n + 1, sizeof(List*)));
My answer is only correct if that is a mistake and you intended
adj = static_cast<List*>(calloc(n + 1, sizeof(List)));