Skip to content

Commit

Permalink
Add blocks & getDILocation func
Browse files Browse the repository at this point in the history
  • Loading branch information
pluralia committed Mar 4, 2024
1 parent 8d13de7 commit c18f470
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 63 deletions.
10 changes: 9 additions & 1 deletion examples/basic.ox
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -85,3 +91,5 @@ fun returnSum(a: number, b: number): number {
}

printSum(3, 2);

print returnSum(32.3, 123.5);
38 changes: 16 additions & 22 deletions src/cli/llvm-ir-generator/block.ts
Original file line number Diff line number Diff line change
@@ -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<string, llvm.AllocaInst>(ir.nameToAlloca);
ir.nameToAlloca = new Map<string, llvm.AllocaInst>();
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);
Expand All @@ -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.`);
}
Expand All @@ -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]);
}

Expand Down
12 changes: 5 additions & 7 deletions src/cli/llvm-ir-generator/expr.ts
Original file line number Diff line number Diff line change
@@ -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)) {
Expand All @@ -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 === '!')
Expand All @@ -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 === '-') {
Expand Down Expand Up @@ -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.`);
Expand All @@ -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.`);
}
}
11 changes: 7 additions & 4 deletions src/cli/llvm-ir-generator/func.ts
Original file line number Diff line number Diff line change
@@ -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)!;
Expand All @@ -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);
}
Expand Down
12 changes: 7 additions & 5 deletions src/cli/llvm-ir-generator/main.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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));

Expand Down
11 changes: 8 additions & 3 deletions src/cli/llvm-ir-generator/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
};
}

Expand Down Expand Up @@ -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,
Expand All @@ -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));
}
22 changes: 8 additions & 14 deletions src/cli/llvm-ir-generator/var.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -29,19 +26,16 @@ 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);
if (!alloca) {
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);

}
14 changes: 7 additions & 7 deletions src/language/ox.langium
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ entry OxProgram:
elements+=OxElement*;

OxElement:
ExpressionBlock |
Block |
IfStatement |
WhileStatement |
ForStatement |
Expand All @@ -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*
'}';

Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit c18f470

Please sign in to comment.