Create global vector using LLVM IR Builder - c++

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);

Related

Pointer to array - initialize element

I have a class Pixel and a class Image with a function used to update a pixel line. I want to initialize the pixel line. My problem is to initialize the array. Actually I have this :
bool UpdateLine(Pixel line[], int nb)
{
bool noError = true;
line = new Pixel[nb];
for (int r = 0; r < nb; r++)
{
line[r] = new Pixel(); // -> line causing troubles
// do some stuff with my pixel
[...]
}
return noError;
}
When I try this I have :
no viable overloaded '='
How can I initialize each elements for my array ?
You actually have two problems.
The first, regarding your error, is because new Pixel() results in a pointer to a Pixel object. In C++ you don't need new to create objects (do you come from a Java or C# background perhaps?). The initial allocation of the array creates the objects for you.
The second problem is that you assign to the pointer variable line, but line is a local variable inside the function. All modification to it will be lost once the function returns, and you will have a memory leak. You need to pass line by reference.
In the future when dealing with collections of a single type of data, I suggest you use std::vector instead. You still need to pass the vector by reference though, if you want to add elements to it.
line[r] = new Pixel(); // -> line causing troubles
line[r] is a Pixel object, not a pointer, so you can't assign a pointer to it.
Why aren't you using a std::vector?

Instantiating a 3D array in C++ using parameters

Sorry if this is a noob question, but I'm currently learning C++. I have a function that takes in several parameters - I would like to use those parameters when creating a 3D int array.
void* testFunction(int countX, int countY, int countZ)
{
const int NX = countX;
const int NY = countY;
const int NZ = countZ;
int* data_out = new int*[NX][NY][NZ];
// The above line throws the error on "NY" - expression must
// have a constant value
}
From various posts I've learned that you must allocate the array first but I guess I'm doing that wrong? How do you properly initialize a multidimensional array. Also, why does the initialization require a pointer?
To explain the error: C++ requires a name of a type in its new operator. A name of a type cannot have runtime dimensions, because all types in C++ are static (determined at compilation time).
For example, this allocates 3 elements of type int[4][5]:
new int[3][4][5];
Another example: this allocates NX elements of type int[4][5]:
new int[NX][4][5];
An incorrect example: this would allocate NX elements of type int[NY][NZ], if C++ had support for "dynamic" types:
new int[NX][NY][NZ];
To allocate a 3-dimensional array, or something that looks like it, you can use std::vector:
std::vector<std::vector<std::vector<int>>> my_3D_array;
... // initialization goes here
my_3D_array[2][2][2] = 222; // whatever you want to do with it
To make the syntax less verbose, and streamline the initialization, use typedef (or here using, which is the same):
using int_1D = std::vector<int>; // this is a 1-dimensional array type
using int_2D = std::vector<int_1D>; // this is a 2-dimensional array type
using int_3D = std::vector<int_2D>; // this is a 3-dimensional array type
int_3D data(NX, int_2D(NY, int_1D(NZ))); // allocate a 3-D array, elements initialized to 0
data[2][2][2] = 222;
If you want to return this array from your function, you should declare it; you cannot just return a void pointer to the data variable. Here is a syntax of a declaration:
using int_1D = std::vector<int>;
using int_2D = std::vector<int_1D>;
using int_3D = std::vector<int_2D>;
int_3D testFunction(int countX, int countY, int countZ)
{
int_3D data(...);
...
return data;
}
That is, instead of using new, just use std::vector<whatever> as if it were any other type.

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];

When should I use ConstantDataArray versus ConstantArray?

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.

The right way to create pointer to pointer object?

What is the right way to create a pointer to pointer object? Like for example,
int **foo;
foo = new int[4][4];
Then the compiler gives me an error saying "cannot convert from int (*)[4] to int **.
Thanks.
int **foo = new int*[4];
for (int i = 0; i < 4; i++)
foo[i] = new int[4];
Clarification:
In many languages the code above is called a jagged array and it's only useful when the "rows" have different sizes. C++ has no direct language support for dynamically allocated rectangular arrays, but it's easy to write it yourself:
int *foo = new int[width * height];
foo[y * height + x] = value;
Using raw new is a bit unwieldy to use. The inner dimension (last 4) must be a compile time constant, in addition. You also have to remember to delete the array once you are finished using it.
int (*foo)[4] = new int[4][4];
foo[2][3] = ...;
delete[] foo;
If that feels too "syntactic braindead", you can use typedef to prettify it
typedef int inner_array[4];
inner_array *foo = new int[4][4];
foo[2][3] = ...;
delete[] foo;
That's called a rectangular 2-dimensional array, because all the rows (4 of them, which can be determined at runtime) have the same width (which must be known at compile time).
Alternatively, use std::vector, with which you don't need to mess around with delete anymore, and which will also handle the raw pointer mess:
std::vector<int> v(4 * 4);
v[index] = ...;
You may later add or remove integers from the vector as you wish. You could also create a vector< vector<int> >, but i find it unwieldy to use, because you have to manage the separate row-vectors (which can be of varying lengths), and they are not seen as "one unit" together.
You can always create a function that maps a two dimensional coordinate to a one-dimensional index
inline int two_dim(int x, int y) {
return y * 4 + x;
}
v[two_dim(2, 3)] = ...;
For a simple two-dimensional array whose size you know beforehand, you don't need new at all, though
int x[4][4];
x[2][3] = ...;