[Bug] CreateAlloca causes segmentation fault
jso8910 opened this issue · 7 comments
Your environment
- OS version: MacOS monterey
- Node.js version: 17.6.0
- LLVM version: 13
Describe the bug
Essentially, if I pass an IRBuilder around a few functions as an argument, (I haven't tested without doing this but I strongly assume that nothing would change if I used it in its original function) CreateAlloca causes a segmentation fault. It used to work and I haven't changed anything about how I use it but it still doesn't work. I have tried manually declaring all of the values so I am sure there is nothing wrong with them. This is how I am using it:
builder.CreateAlloca(builder.getInt64Ty(), null, name)
All I get is a segmentation fault. I have run SetInsertPoint
if you are wondering.
Expected behavior
No segmentation fault, a variable to be created
The information you gave is too little. I can't find out the cause of the problem. Please give the complete code and the screenshot of error reporting.
The error:
Zsh: segmentation fault
That's not exact but there is no information.
The code? That's it. Everything else works and doesn't cause errors and is the code I used that used to work before I randomly got segmentation faults. If you want to know I can tell you the basics of the code (I can't show you because I am on my phone).
I initialize context, builder, and module, do processing of an object, run a function with builder and context as an argument, that function creates an llvm function and then runs another function with builder and context as a function which creates a block called init
and I run builder.SetInsertPoint(init)
. This isn't exactly where the variable assignment normally is but I put the variable assignment here to eliminate the possibility of bugs elsewhere. At this point there is a segmentation fault.
Here is the relevant code now that I am on my computer:
Initializing the builder
const context = new llvm.LLVMContext();
const module = new llvm.Module("demo", context);
const builder = new llvm.IRBuilder(context);
Where I run the CreateAlloca
const init = llvm.BasicBlock.Create(context, "init", parent);
const cond = llvm.BasicBlock.Create(context, "condition", parent);
const body = llvm.BasicBlock.Create(context, "body", parent);
const after_body = llvm.BasicBlock.Create(context, "after", parent);
const end = llvm.BasicBlock.Create(context, "end", parent);
parent.addBasicBlock(init)
builder.SetInsertPoint(init)
builder.CreateAlloca(builder.getInt64Ty(), null, name)
The code you gave is not enough, please give all the code you wrote.
In addition, I used your sample to write the following code, it works fine.
import path from 'path';
import llvm from 'llvm-bindings';
const filename = path.basename(__filename);
const context = new llvm.LLVMContext();
const module = new llvm.Module("demo", context);
const builder = new llvm.IRBuilder(context);
const returnType = builder.getVoidTy();
const functionType = llvm.FunctionType.get(returnType, false);
const parent = llvm.Function.Create(functionType, llvm.Function.LinkageTypes.ExternalLinkage, 'issue13', module);
const init = llvm.BasicBlock.Create(context, "init", parent);
builder.SetInsertPoint(init)
builder.CreateAlloca(builder.getInt64Ty(), null, 'alloca')
builder.CreateRetVoid();
if (llvm.verifyFunction(parent)) {
console.error(`${filename}: verifying the 'add' function failed`);
process.exit(1);
}
if (llvm.verifyModule(module)) {
console.error(`${filename}: verifying the module failed`);
process.exit(1);
}
console.log(module.print());
Here is all of my code used to generate the llvm. The code I am testing will run the create_function function and, within it, call create_for. One variable (i=0) should be assigned inside the init section.
interface Var_List {
[keys: string]: llvm.AllocaInst
}
interface LooseType {
[keys: string]: llvm.Type
}
interface LooseValue {
[keys: string]: Function
}
const VARS: Var_List = {}
const TypeByName: LooseType = {}
const TypeValueByName: LooseValue = {}
const gen_llvm = (ast: ts.NodeArray<ts.Node>, typeChecker: ts.TypeChecker) => {
const context = new llvm.LLVMContext();
const module = new llvm.Module("demo", context);
const builder = new llvm.IRBuilder(context);
// Get type objects
TypeByName.i16 = builder.getInt16Ty();
TypeByName.i32 = builder.getInt32Ty();
TypeByName.i64 = builder.getInt64Ty();
// Get function to create value
TypeValueByName.i16 = builder.getInt16;
TypeValueByName.i32 = builder.getInt32;
TypeValueByName.i64 = builder.getInt64;
// TODO: add bool, char, float, etc
ast.forEach(node => {
switch (node.kind) {
// TODO: add more to switch and eventually make this into a function which contains every possible child and how to handle it
case SyntaxKind.FunctionDeclaration:
create_function((node as FunctionDeclaration), builder, context, typeChecker)
}
})
return module
}
// TODO add a return type and parameters
const create_function = (node: FunctionDeclaration, builder: llvm.IRBuilder, context: llvm.LLVMContext, typeChecker: ts.TypeChecker) => {
let funcName = node.name?.kind == SyntaxKind.Identifier && node.name?.escapedText;
if (!funcName) {
return; // Add something empty of a type idk
}
const returnType = builder.getInt32Ty();
const funcType = llvm.FunctionType.get(returnType, false);
const func = llvm.Function.Create(
funcType,
llvm.Function.LinkageTypes.ExternalLinkage,
funcName);
const fnStart = llvm.BasicBlock.Create(context, "start", func);
builder.SetInsertPoint(fnStart);
const body = node.body;
if (!body || body?.kind !== SyntaxKind.Block) {
return;
}
body.forEachChild(node => {
switch (node.kind) {
case SyntaxKind.ForStatement:
create_for((node as ForStatement), builder, context, typeChecker);
}
})
// TODO have a proper return
builder.CreateRet(builder.getInt32(0))
}
const var_dec_list = (node: VariableDeclarationList, builder: llvm.IRBuilder, context: llvm.LLVMContext, typeChecker: ts.TypeChecker) => {
node.declarations.forEach(var_dec => {
if (var_dec.name.kind === SyntaxKind.Identifier) {
console.log("hi")
const name = var_dec.name.escapedText;
let value = null;
switch (var_dec.initializer?.kind) {
// TODO: Add more, make function work for stuff that isnt a number
case SyntaxKind.NumericLiteral:
value = parseInt((var_dec.initializer as ts.NumericLiteral).text);
// ? consider how to distinguish between char and string.
}
// ? Do we need to check for valid variable names? How should we lint?
if (typeof value !== "undefined" && name && typeof name !== "undefined" && var_dec.initializer) {
console.log("hi")
let type = typeChecker.typeToString(typeChecker.getTypeAtLocation(var_dec.initializer));
if (!(type in TypeByName)) {
type = "i64" // TODO: should this be a float? Probably but idk exactly how to do floats rn
}
VARS[name] = create_primitive_var(name, TypeByName[type], builder)
// VARS[name] = builder.CreateAlloca(TypeByName[type], null, name)
console.log("seg")
set_var(TypeValueByName[type](value), VARS[name], builder)
}
}
})
}
const create_primitive_var = (name: string, type: llvm.Type, builder: llvm.IRBuilder) => {
console.log(!!builder, type, name)
// ! DO NOT PUSH https://github.com/ApsarasX/llvm-bindings/issues/13
return builder.CreateAlloca(type, undefined, name)
}
const set_var = (value: llvm.Value, var_alloc: llvm.AllocaInst, builder: llvm.IRBuilder) => {
if (!llvm.Type.isSameType(value.getType(), var_alloc.getType().getPointerElementType())) {
throw TypeError(`Variable types do not match. Cannot change type of a variable`); // TODO: better message
}
builder.CreateStore(value, var_alloc);
}
export const create_for = (node: ForStatement, builder: llvm.IRBuilder, context: llvm.LLVMContext, typeChecker: ts.TypeChecker) => {
const parent = builder.GetInsertBlock()?.getParent()
if (!parent) {
return
}
const init = llvm.BasicBlock.Create(context, "init", parent);
const cond = llvm.BasicBlock.Create(context, "condition", parent);
const body = llvm.BasicBlock.Create(context, "body", parent);
const after_body = llvm.BasicBlock.Create(context, "after", parent);
const end = llvm.BasicBlock.Create(context, "end", parent);
parent.addBasicBlock(init)
builder.SetInsertPoint(init)
if (node.initializer) {
console.log("here")
switch (node.initializer.kind) {
case SyntaxKind.VariableDeclarationList:
console.log("here")
var_dec_list((node.initializer as unknown as ts.VariableDeclarationList), builder, context, typeChecker);
console.log(VARS)
// TODO more cases, put in function
}
}
// VARS['i'] = create_primitive_var('i', builder.getInt32Ty(), builder)
// set_var(builder.getInt32(0), VARS['i'], builder)
builder.CreateBr(cond);
parent.addBasicBlock(cond)
builder.SetInsertPoint(cond)
const condBuilder = new llvm.IRBuilder(cond);
const condCheck = condBuilder.CreateICmpSLT(builder.CreateLoad(VARS['i'].getType().getPointerElementType(), VARS['i'], 'i'), builder.getInt32(5), 'condition');
builder.CreateCondBr(condCheck, body, end)
parent.addBasicBlock(body);
// TODO the content here
builder.CreateBr(after_body);
parent.addBasicBlock(after_body);
builder.SetInsertPoint(after_body);
const inc_res = builder.CreateAdd(builder.CreateLoad(builder.getInt32Ty(), VARS['i']), builder.getInt32(1), 'inc_i')
builder.CreateStore(inc_res, VARS['i'])
}
export default gen_llvm;
There are 4 errors in your sample code.
Error 1
When using llvm.Function.Create, you should pass the 4th argument as module.
The corrent code is shown below.
const func = llvm.Function.Create(
funcType,
llvm.Function.LinkageTypes.ExternalLinkage,
- funcName);
+ funcName,
+ module);
Error 2
All llvm methods are imported from C++ addon. If you write like above, you will lose this
pointer.
The corrent code is shown below.
- TypeValueByName.i16 = builder.getInt16;
- TypeValueByName.i32 = builder.getInt32;
- TypeValueByName.i64 = builder.getInt64;
+ TypeValueByName.i16 = builder.getInt16.bind(builder);
+ TypeValueByName.i32 = builder.getInt32.bind(builder);
+ TypeValueByName.i64 = builder.getInt64.bind(builder);
Error 3
You passed two different types of llvm values to CreateICmpSLT
method.
The corrent code is shown below.
- const condCheck = condBuilder.CreateICmpSLT(builder.CreateLoad(VARS['i'].getType().getPointerElementType(), VARS['i'], 'i'), builder.getInt32(5), 'condition');
+ const condCheck = condBuilder.CreateICmpSLT(builder.CreateLoad(VARS['i'].getType().getPointerElementType(), VARS['i'], 'i'), builder.getInt64(5), 'condition');
Error 4
If you pass the third parameter when creating a basic block using llvm.BasicBlock.Create
, you should not call the addBasicBlock
method extraly. They are in conflict with each other.
- parent.addBasicBlock(init);
- parent.addBasicBlock(cond);
- parent.addBasicBlock(body);
- parent.addBasicBlock(after_body);
it's solved, please close.