From c18f4703cf5a4e2228a42a28c391ced52d73e715 Mon Sep 17 00:00:00 2001 From: pluralia Date: Mon, 4 Mar 2024 13:07:58 +0100 Subject: [PATCH] Add blocks & getDILocation func --- examples/basic.ox | 10 +++++++- src/cli/llvm-ir-generator/block.ts | 38 +++++++++++++----------------- src/cli/llvm-ir-generator/expr.ts | 12 ++++------ src/cli/llvm-ir-generator/func.ts | 11 +++++---- src/cli/llvm-ir-generator/main.ts | 12 ++++++---- src/cli/llvm-ir-generator/util.ts | 11 ++++++--- src/cli/llvm-ir-generator/var.ts | 22 +++++++---------- src/language/ox.langium | 14 +++++------ 8 files changed, 67 insertions(+), 63 deletions(-) diff --git a/examples/basic.ox b/examples/basic.ox index a224aeb..320f03e 100644 --- a/examples/basic.ox +++ b/examples/basic.ox @@ -18,6 +18,7 @@ var add: number = 23 + 41; var subtract: number = 13 - 4; var multiply: number = 13 * 4; var divide: number = 62 / 2; +var fractional: number = 61 / 3; var negateMe: number = -add; @@ -48,12 +49,17 @@ min = 5; // Printing print average; +var x: boolean = true; +print x; // prints 1 + // Blocks { // print "This is a new block"; var x: number = 15; + print x; // prints 15 } -// print "`x` isn't available in this scope"; + +print x; // prints 1 // // Control flow // // If branching @@ -85,3 +91,5 @@ fun returnSum(a: number, b: number): number { } printSum(3, 2); + +print returnSum(32.3, 123.5); diff --git a/src/cli/llvm-ir-generator/block.ts b/src/cli/llvm-ir-generator/block.ts index edcc487..42a8b4f 100644 --- a/src/cli/llvm-ir-generator/block.ts +++ b/src/cli/llvm-ir-generator/block.ts @@ -1,25 +1,21 @@ import llvm from "llvm-bindings"; -import { OxElement, ReturnStatement, Parameter, isFunctionDeclaration, isReturnStatement, isVariableDeclaration, isPrintStatement, PrintStatement, isMemberCall, isExpression, isAssignmentStatement } from "../../language/generated/ast.js"; +import { OxElement, ReturnStatement, Parameter, isFunctionDeclaration, isReturnStatement, isVariableDeclaration, isPrintStatement, PrintStatement, isMemberCall, isExpression, isAssignmentStatement, isBlock } from "../../language/generated/ast.js"; import { generateFunction } from "./func.js"; -import { DI, IR, getLoc, getCurrScope } from "./util.js"; +import { DI, IR, getPos, getCurrScope, getDILocation } from "./util.js"; import { generateAssignment, generateVariableDeclaration } from "./var.js"; import { generateExpression, generateMemberCall } from "./expr.js"; -export type BlockInfo = { - name: string, - func?: llvm.Function, - inputVars?: Parameter[] +export type FuncInfo = { + func: llvm.Function, + inputVars: Parameter[] } -export function generateExpressionBlock(ir: IR, di: DI, elements: OxElement[], - { name, func, inputVars }: BlockInfo) { - - const entryBB = llvm.BasicBlock.Create(ir.context, name, func); - ir.builder.SetInsertPoint(entryBB); - +export function generateBlock(ir: IR, di: DI, elements: OxElement[], funcInfo?: FuncInfo) { const backupNamedValues = new Map(ir.nameToAlloca); ir.nameToAlloca = new Map(); - if (func && inputVars) { + + if (funcInfo) { + const { func, inputVars } = funcInfo; for (let i = 0; i < inputVars.length; i++) { const alloca = generateParameter(ir, di, inputVars[i], i, func); ir.nameToAlloca.set(inputVars[i].name, alloca); @@ -42,6 +38,8 @@ export function generateExpressionBlock(ir: IR, di: DI, elements: OxElement[], generateMemberCall(ir, di, elem); } // skip otherwise + } else if (isBlock(elem)) { + generateBlock(ir, di, elem.elements); } else { throw new Error(`Statement ${elem.$cstNode?.text} is not supported.`); } @@ -52,43 +50,39 @@ export function generateExpressionBlock(ir: IR, di: DI, elements: OxElement[], function generateParameter(ir: IR, di: DI, param: Parameter, i: number, func: llvm.Function): llvm.AllocaInst { const { name, type } = param; - const { line, col } = getLoc(param); + const { line } = getPos(param); const alloca = ir.builder.CreateAlloca(ir.basicTypes.get(type.primitive)!, null, name); ir.builder.CreateStore(func.getArg(i), alloca); - const diVarType = di.basicTypes.get(param.type.primitive)!; - const diLocalVar = di.builder.createParameterVariable(getCurrScope(di), name, i + 1, di.file, line, diVarType); + const diLocalVar = di.builder.createParameterVariable(getCurrScope(di), name, i + 1, di.file, line, di.basicTypes.get(param.type.primitive)!); di.builder.insertDeclare( alloca, diLocalVar, di.builder.createExpression(), - llvm.DILocation.get(ir.context, line, col, getCurrScope(di)), ir.builder.GetInsertBlock()! + getDILocation(ir, di, param), ir.builder.GetInsertBlock()! ); return alloca; } function generateReturn(ir: IR, di: DI, ret: ReturnStatement) { - const { line, col } = getLoc(ret); - const value = ret.value ? generateExpression(ir, di, ret.value) : ir.builder.getInt32(0); - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, line, col, getCurrScope(di))); + ir.builder.SetCurrentDebugLocation(getDILocation(ir, di, ret)); ir.builder.CreateRet(value); } function generatePrintCall(ir: IR, di: DI, elem: PrintStatement) { const printfFn = ir.module.getFunction('printf')!; if (printfFn) { - const { line, col } = getLoc(elem); const value = generateExpression(ir, di, elem.value); const modifier = value.getType().getTypeID() === 13 ? ir.globalValues.get('integer_modifier') : ir.globalValues.get('float_modifier'); - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, line, col, getCurrScope(di))); + ir.builder.SetCurrentDebugLocation(getDILocation(ir, di, elem)); return ir.builder.CreateCall(printfFn, [modifier!, value]); } diff --git a/src/cli/llvm-ir-generator/expr.ts b/src/cli/llvm-ir-generator/expr.ts index 6e9c00e..e8808bf 100644 --- a/src/cli/llvm-ir-generator/expr.ts +++ b/src/cli/llvm-ir-generator/expr.ts @@ -1,9 +1,11 @@ import llvm from "llvm-bindings"; import { BinaryExpression, Expression, MemberCall, UnaryExpression, isBooleanExpression, isFunctionDeclaration, isMemberCall, isNumberExpression, isUnaryExpression } from "../../language/generated/ast.js"; -import { DI, IR, getLoc, getCurrScope } from "./util.js"; +import { DI, IR, getDILocation } from "./util.js"; export function generateExpression(ir: IR, di: DI, expr: Expression): llvm.Value { + ir.builder.SetCurrentDebugLocation(getDILocation(ir, di, expr)); + if (isNumberExpression(expr)) { return llvm.ConstantFP.get(ir.builder.getDoubleTy(), expr.value); } else if (isBooleanExpression(expr)) { @@ -18,11 +20,9 @@ export function generateExpression(ir: IR, di: DI, expr: Expression): llvm.Value } function generateUnaryExpr(ir: IR, di: DI, expr: UnaryExpression): llvm.Value { - const { line, col } = getLoc(expr); const value = generateExpression(ir, di, expr.value); const op = expr.operator; - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, line, col, getCurrScope(di))); if (op === '-') { return ir.builder.CreateFNeg(value); } else { // (op === '!') @@ -31,12 +31,10 @@ function generateUnaryExpr(ir: IR, di: DI, expr: UnaryExpression): llvm.Value { } function generateBinaryExpr(ir: IR, di: DI, expr: BinaryExpression): llvm.Value { - const loc = getLoc(expr); const left = generateExpression(ir, di, expr.left); const right = generateExpression(ir, di, expr.right); const op = expr.operator; - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, loc.line, loc.col, getCurrScope(di))); if (op === '+') { return ir.builder.CreateFAdd(left, right); } else if (op === '-') { @@ -72,12 +70,11 @@ function generateBinaryExpr(ir: IR, di: DI, expr: BinaryExpression): llvm.Value export function generateMemberCall(ir: IR, di: DI, expr: MemberCall): llvm.Value { const member = expr.element.ref!; - const { line, col } = getLoc(expr); + ir.builder.SetCurrentDebugLocation(getDILocation(ir, di, expr)); if (isFunctionDeclaration(member)) { const func = ir.module.getFunction(member.name); if (func) { - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, line, col, getCurrScope(di))); return ir.builder.CreateCall(func, expr.arguments.map(a => generateExpression(ir, di, a))); } throw new Error(`LLVM IR generation: Function '${member.name}' is not in scope.`); @@ -94,6 +91,7 @@ export function generateMemberCall(ir: IR, di: DI, expr: MemberCall): llvm.Value if (alloca) { return ir.builder.CreateLoad(alloca.getAllocatedType(), alloca); } + throw new Error(`LLVM IR generation: Variable '${varName}' is not in scope.`); } } diff --git a/src/cli/llvm-ir-generator/func.ts b/src/cli/llvm-ir-generator/func.ts index 9001310..6fbbeb5 100644 --- a/src/cli/llvm-ir-generator/func.ts +++ b/src/cli/llvm-ir-generator/func.ts @@ -1,11 +1,11 @@ import llvm from "llvm-bindings"; import { FunctionDeclaration } from "../../language/generated/ast.js"; -import { DI, IR, getLoc, getCurrScope } from "./util.js"; -import { generateExpressionBlock } from "./block.js"; +import { DI, IR, getPos, getCurrScope } from "./util.js"; +import { generateBlock } from "./block.js"; export function generateFunction(ir: IR, di: DI, funcDecl: FunctionDeclaration) { const { name, parameters, returnType, body } = funcDecl; - const { line } = getLoc(funcDecl); + const { line } = getPos(funcDecl); const signature = getSignature(funcDecl); const funcReturnType = ir.basicTypes.get(returnType.primitive)!; @@ -25,8 +25,11 @@ export function generateFunction(ir: IR, di: DI, funcDecl: FunctionDeclaration) di.scope.push(debugInfoFuncSubprogram); - generateExpressionBlock(ir, di, body.elements, { name: 'entry', func, inputVars: parameters }); + const entryBB = llvm.BasicBlock.Create(ir.context, 'entry', func); + ir.builder.SetInsertPoint(entryBB); + generateBlock(ir, di, body.elements, { func, inputVars: parameters }); + // if the return type is not void, the return statement was generated in the `generateBlock` if (funcDecl.returnType.primitive === 'void') { generateReturnVoid(ir, di, funcDecl); } diff --git a/src/cli/llvm-ir-generator/main.ts b/src/cli/llvm-ir-generator/main.ts index d474b29..1f7b7ed 100644 --- a/src/cli/llvm-ir-generator/main.ts +++ b/src/cli/llvm-ir-generator/main.ts @@ -1,7 +1,7 @@ import { FunctionDeclaration, isFunctionDeclaration, type OxProgram } from '../../language/generated/ast.js'; import llvm from 'llvm-bindings'; import { DI, IR, initDI, initIR, setupExternFunctions } from './util.js'; -import { generateExpressionBlock } from './block.js'; +import { generateBlock } from './block.js'; import { initDITypes, initIRTypes } from './type.js'; import { generateFunction } from './func.js'; @@ -44,14 +44,16 @@ function generateMainFunc(ir: IR, di: DI, program: OxProgram) { di.scope.push(debugInfoMainFuncSubprogram); - generateExpressionBlock(ir, di, - program.elements.filter(e => !isFunctionDeclaration(e)), - { name: 'entry', func: mainFunc } + const entryBB = llvm.BasicBlock.Create(ir.context, 'entry', mainFunc); + ir.builder.SetInsertPoint(entryBB); + + generateBlock(ir, di, + program.elements.filter(e => !isFunctionDeclaration(e)), { func: mainFunc, inputVars: [] } ); // generates an artificial `return 0` statement; // it's not represented in the source code, and debugging points on a non-existing line - const endLine = program.$cstNode?.range.end.line! + 2; + const endLine = program.$cstNode?.range.end.line! + 1; ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, endLine, 1, debugInfoMainFuncSubprogram)); ir.builder.CreateRet(ir.builder.getInt32(0)); diff --git a/src/cli/llvm-ir-generator/util.ts b/src/cli/llvm-ir-generator/util.ts index e2adad9..26b9be7 100644 --- a/src/cli/llvm-ir-generator/util.ts +++ b/src/cli/llvm-ir-generator/util.ts @@ -25,7 +25,7 @@ export function initIR(filename: string): IR { return { context, module, builder, basicTypes: new Map(), nameToAlloca: new Map(), - globalValues: new Map() + globalValues: new Map(), }; } @@ -65,12 +65,12 @@ export function setupExternFunctions(ir: IR) { )); } -export type Loc = { +export type Pos = { line: number, col: number } -export function getLoc(node: AstNode): Loc { +export function getPos(node: AstNode): Pos { const pos = node.$cstNode!.range.start; return { line: pos.line + 1, @@ -81,3 +81,8 @@ export function getLoc(node: AstNode): Loc { export function getCurrScope(di: DI): llvm.DIScope { return di.scope[di.scope.length - 1]; } + +export function getDILocation(ir: IR, di: DI, node: AstNode): llvm.DILocation { + const { line, col } = getPos(node); + return llvm.DILocation.get(ir.context, line, col, getCurrScope(di)); +} diff --git a/src/cli/llvm-ir-generator/var.ts b/src/cli/llvm-ir-generator/var.ts index d6571d2..62c46c8 100644 --- a/src/cli/llvm-ir-generator/var.ts +++ b/src/cli/llvm-ir-generator/var.ts @@ -1,25 +1,22 @@ import llvm from "llvm-bindings"; import { AssignmentStatement, VariableDeclaration } from "../../language/generated/ast.js"; import { generateExpression } from "./expr.js"; -import { DI, IR, getLoc, getCurrScope } from "./util.js"; +import { DI, IR, getPos, getCurrScope, getDILocation } from "./util.js"; export function generateVariableDeclaration(ir: IR, di: DI, varDecl: VariableDeclaration) { const { name, type, value: expr } = varDecl; const irType = ir.basicTypes.get(type.primitive)!; - const { line, col } = getLoc(varDecl); - const scope = getCurrScope(di); + const { line } = getPos(varDecl); + const diLoc = getDILocation(ir, di, varDecl); const alloca = ir.builder.CreateAlloca(irType, null, name); - const diLocalVar = di.builder.createAutoVariable(scope, name, di.file, line, di.basicTypes.get(type.primitive)!); + const diLocalVar = di.builder.createAutoVariable(getCurrScope(di), name, di.file, line, di.basicTypes.get(type.primitive)!); di.builder.insertDeclare( - alloca, - diLocalVar, - di.builder.createExpression(), - llvm.DILocation.get(ir.context, line, col, scope), - ir.builder.GetInsertBlock()! + alloca, diLocalVar, di.builder.createExpression(), + diLoc, ir.builder.GetInsertBlock()! ); - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, line, col, scope)); + ir.builder.SetCurrentDebugLocation(diLoc); const value = expr ? generateExpression(ir, di, expr) : llvm.Constant.getNullValue(irType); ir.builder.CreateStore(value, alloca); @@ -29,8 +26,6 @@ export function generateVariableDeclaration(ir: IR, di: DI, varDecl: VariableDec export function generateAssignment(ir: IR, di: DI, assign: AssignmentStatement) { const { varRef, value: expr } = assign; - const { line, col } = getLoc(assign); - const scope = getCurrScope(di); const name = varRef.ref?.name!; const alloca = ir.nameToAlloca.get(name); @@ -38,10 +33,9 @@ export function generateAssignment(ir: IR, di: DI, assign: AssignmentStatement) throw new Error(`LLVM IR generation: assignment to not existing variable '${name}'.`); } - ir.builder.SetCurrentDebugLocation(llvm.DILocation.get(ir.context, line, col, scope)); + ir.builder.SetCurrentDebugLocation(getDILocation(ir, di, assign)); // store a new value const value = generateExpression(ir, di, expr) ir.builder.CreateStore(value, alloca); - } diff --git a/src/language/ox.langium b/src/language/ox.langium index daa7ec6..e6d9b21 100644 --- a/src/language/ox.langium +++ b/src/language/ox.langium @@ -4,7 +4,7 @@ entry OxProgram: elements+=OxElement*; OxElement: - ExpressionBlock | + Block | IfStatement | WhileStatement | ForStatement | @@ -17,23 +17,23 @@ OxElement: ; IfStatement: - 'if' '(' condition=Expression ')' block=ExpressionBlock - ('else' elseBlock=ExpressionBlock)? + 'if' '(' condition=Expression ')' block=Block + ('else' elseBlock=Block)? ; WhileStatement: - 'while' '(' condition=Expression ')' block=ExpressionBlock + 'while' '(' condition=Expression ')' block=Block ; ForStatement: - 'for' '(' counter=VariableDeclaration? ';' condition=Expression? ';' execution=Expression? ')' block=ExpressionBlock + 'for' '(' counter=VariableDeclaration? ';' condition=Expression? ';' execution=Expression? ')' block=Block ; PrintStatement: 'print' value=Expression; ReturnStatement: 'return' value=Expression?; -ExpressionBlock: '{' +Block: '{' elements+=OxElement* '}'; @@ -83,7 +83,7 @@ NumberExpression: value=NUMBER; BooleanExpression: value?='true' | 'false'; FunctionDeclaration: - 'fun' name=ID '(' (parameters+=Parameter (',' parameters+=Parameter)*)? ')' ':' returnType=TypeReference body=ExpressionBlock; + 'fun' name=ID '(' (parameters+=Parameter (',' parameters+=Parameter)*)? ')' ':' returnType=TypeReference body=Block; Parameter: name=ID ':' type=TypeReference;