From 76eb5fdd4858e1844fce2a53af3287acdb7f53c4 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Wed, 20 Dec 2023 16:55:06 +0000 Subject: Rename LlvmPatioCodeGent to PatioToLlvm --- .../compiler/backends/llvm/LlvmBackend.java | 6 +- .../backends/llvm/codegen/LlvmPatioCodeGen.java | 1289 -------------------- .../backends/llvm/codegen/PatioToLlvm.java | 1289 ++++++++++++++++++++ 3 files changed, 1292 insertions(+), 1292 deletions(-) delete mode 100644 src/main/java/org/shedlang/compiler/backends/llvm/codegen/LlvmPatioCodeGen.java create mode 100644 src/main/java/org/shedlang/compiler/backends/llvm/codegen/PatioToLlvm.java (limited to 'src/main/java') diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmBackend.java b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmBackend.java index 2c40bc78..b557b866 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmBackend.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmBackend.java @@ -1,7 +1,7 @@ package org.shedlang.compiler.backends.llvm; import org.shedlang.compiler.backends.Backend; -import org.shedlang.compiler.backends.llvm.codegen.LlvmPatioCodeGen; +import org.shedlang.compiler.backends.llvm.codegen.PatioToLlvm; import org.shedlang.compiler.backends.llvm.codegen.LlvmPatioCodeGenGlobalContext; import org.shedlang.compiler.backends.llvm.ir.ast.LlvmBasicBlock; import org.shedlang.compiler.backends.llvm.ir.ast.LlvmFunctionDefinition; @@ -59,7 +59,7 @@ public class LlvmBackend implements Backend { PatioCodeGenContext.forTypeInfos(typeInfos) ); - var llvmModuleResult = LlvmPatioCodeGen.generateCompilationUnit( + var llvmModuleResult = PatioToLlvm.generateCompilationUnit( patioCompilationUnit, globalContext ); @@ -117,7 +117,7 @@ public class LlvmBackend implements Backend { var patioCompilationUnit = context.toCompilationUnit(source); - var llvmModuleResult = LlvmPatioCodeGen.generateCompilationUnit( + var llvmModuleResult = PatioToLlvm.generateCompilationUnit( patioCompilationUnit, globalContext ); diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/codegen/LlvmPatioCodeGen.java b/src/main/java/org/shedlang/compiler/backends/llvm/codegen/LlvmPatioCodeGen.java deleted file mode 100644 index a680f3b7..00000000 --- a/src/main/java/org/shedlang/compiler/backends/llvm/codegen/LlvmPatioCodeGen.java +++ /dev/null @@ -1,1289 +0,0 @@ -package org.shedlang.compiler.backends.llvm.codegen; - -import org.pcollections.PVector; -import org.pcollections.TreePVector; -import org.shedlang.compiler.backends.llvm.LlvmCodeGenLocalVariableSpace; -import org.shedlang.compiler.backends.llvm.codegen.errors.SizeofJmpBufError; -import org.shedlang.compiler.backends.llvm.ir.ast.*; -import org.shedlang.compiler.backends.llvm.ir.ast.instructions.*; -import org.shedlang.compiler.backends.llvm.ir.ast.operands.*; -import org.shedlang.compiler.backends.llvm.ir.ast.types.LlvmType; -import org.shedlang.compiler.backends.llvm.ir.ast.types.LlvmTypes; -import org.shedlang.compiler.filesystem.ShedCompilerFiles; -import org.shedlang.compiler.patio.ast.*; -import org.shedlang.compiler.patio.ast.instructions.*; -import org.shedlang.compiler.patio.ast.operands.*; -import org.shedlang.compiler.patio.ast.types.*; -import org.shedlang.compiler.util.Lazy; -import org.shedlang.compiler.util.Lists; -import org.shedlang.compiler.util.P; - -import java.io.IOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.StreamSupport; - -public class LlvmPatioCodeGen { - private static final LlvmFunctionDeclaration ALLOC_DECLARATION = new LlvmFunctionDeclaration( - "shed__runtime__gc_alloc", - LlvmTypes.pointer(LlvmTypes.INT_8), - List.of( - new LlvmParam(LlvmTypes.INT_64, "size"), - new LlvmParam(LlvmTypes.INT_64, "children_offset"), - new LlvmParam(LlvmTypes.INT_64, "children_count") - ) - ); - - private static final LlvmType EXCEPTION_TAG_TYPE = LlvmTypes.INT_32; - private static final LlvmType EXCEPTION_VALUE_TYPE = LlvmTypes.INT_64; - - private static final Lazy JMP_BUF_SIZE = Lazy.of(() -> readJmpBufSize()); - - private static int readJmpBufSize() { - try { - var sizeofJmpBufString = Files.readString( - ShedCompilerFiles.getRoot().resolve("runtime/target/amd64/sizeof_jmp_buf.txt") - ); - return Integer.parseInt(sizeofJmpBufString.strip()); - } catch (IOException e) { - throw new SizeofJmpBufError(); - } - } - - private static LlvmType jmpBufType() { - return LlvmTypes.array(JMP_BUF_SIZE.get(), LlvmTypes.INT_8); - } - - private static final LlvmFunctionDeclaration SETJMP_DECLARATION = new LlvmFunctionDeclaration( - "_setjmp", - LlvmTypes.INT_32, - List.of( - new LlvmParam(LlvmTypes.pointer(LlvmTypes.VOID), "env") - ) - ); - - private static final LlvmGlobalDefinition EXCEPTION_HANDLER_DECLARATION = new LlvmGlobalDefinition( - "patio__runtime__exceptions_exception_handler", - LlvmTypes.pointer(LlvmTypes.VOID), - Optional.empty(), - false, - false, - false - ); - - private static final LlvmGlobalDefinition EXCEPTION_VALUE_DECLARATION = new LlvmGlobalDefinition( - "patio__runtime__exceptions_exception_value", - LlvmTypes.INT_64, - Optional.empty(), - false, - false, - false - ); - - private static final LlvmFunctionDeclaration THROW_DECLARATION = new LlvmFunctionDeclaration( - "patio__runtime__exceptions_throw", - LlvmTypes.pointer(LlvmTypes.VOID), - List.of( - new LlvmParam(EXCEPTION_TAG_TYPE, "exception_tag"), - new LlvmParam(EXCEPTION_VALUE_TYPE, "exception_value") - ) - ); - - private static final LlvmFunctionDeclaration RETHROW_DECLARATION = new LlvmFunctionDeclaration( - "patio__runtime__exceptions_rethrow", - LlvmTypes.pointer(LlvmTypes.VOID), - List.of( - new LlvmParam(EXCEPTION_TAG_TYPE, "exception_tag") - ) - ); - - public static LlvmPatioCodeGenGlobalContext.Result generateCompilationUnit( - PatioCompilationUnit patioCompilationUnit, - LlvmPatioCodeGenGlobalContext globalContext - ) { - var context = globalContext.enterModule() - .addTopLevelEntity(ALLOC_DECLARATION) - .addTopLevelEntity(SETJMP_DECLARATION) - .addTopLevelEntity(EXCEPTION_HANDLER_DECLARATION) - .addTopLevelEntity(EXCEPTION_VALUE_DECLARATION) - .addTopLevelEntity(THROW_DECLARATION) - .addTopLevelEntity(RETHROW_DECLARATION); - - for (var patioCompilationUnitElement : patioCompilationUnit.elements()) { - switch (patioCompilationUnitElement) { - case PatioDataElementImmutable patioDataElementImmutable -> { - } - case PatioDataElementMutable patioDataElementMutable -> { - } - case PatioFunctionDeclaration patioFunctionDeclaration -> { - context = context.addFunctionNameToReturnType( - patioFunctionDeclaration.name(), - patioTypeToLlvmType(patioFunctionDeclaration.returnType()) - ); - } - case PatioExceptionTypeDefinition patioExceptionTypeDefinition -> { - // TODO: this needs to be done across all compilation units to ensure - // consistency in tags and therefore allow cross-compilation unit throws. - - context = context.allocateExceptionTag(patioExceptionTypeDefinition.name()); - } - case PatioFunctionDefinition patioFunctionDefinition -> { - context = context.addFunctionNameToReturnType( - patioFunctionDefinition.name(), - patioTypeToLlvmType(patioFunctionDefinition.returnType()) - ); - } - } - } - - for (var patioCompilationUnitElement : patioCompilationUnit.elements()) { - context = generateCompilationUnitElement(patioCompilationUnitElement, context); - } - - return context.toModule(); - } - - private static LlvmPatioCodeGenModuleContext generateCompilationUnitElement( - PatioCompilationUnitElement patioCompilationUnitElement, - LlvmPatioCodeGenModuleContext context - ) { - return switch (patioCompilationUnitElement) { - case PatioDataElementImmutable patioDataElementImmutable -> - generateDataElementImmutable(patioDataElementImmutable, context); - - case PatioDataElementMutable patioDataElementMutable -> - generateDataElementMutable(patioDataElementMutable, context); - - case PatioExceptionTypeDefinition patioExceptionTypeDefinition -> - context; - - case PatioFunctionDeclaration patioFunctionDeclaration -> - generateFunctionDeclaration(patioFunctionDeclaration, context); - - case PatioFunctionDefinition patioFunctionDefinition -> - generateFunctionDefinition(patioFunctionDefinition, context); - }; - } - - private static LlvmPatioCodeGenModuleContext generateDataElementImmutable( - PatioDataElementImmutable patioDataElementImmutable, - LlvmPatioCodeGenModuleContext context - ) { - return context.addTopLevelEntity(new LlvmGlobalDefinition( - patioDataElementImmutable.name(), - patioDataLayoutToLlvmType(patioDataElementImmutable.value().layout()), - Optional.of(patioDataValueToLlvmOperand(patioDataElementImmutable.value())), - true, - true, - true - )); - } - - private static LlvmPatioCodeGenModuleContext generateDataElementMutable( - PatioDataElementMutable patioDataElementMutable, - LlvmPatioCodeGenModuleContext context - ) { - return context.addTopLevelEntity(new LlvmGlobalDefinition( - patioDataElementMutable.name(), - patioDataLayoutToLlvmType(patioDataElementMutable.initialValue().layout()), - Optional.of(patioDataValueToLlvmOperand(patioDataElementMutable.initialValue())), - false, - true, - true - )); - } - - private static LlvmPatioCodeGenModuleContext generateFunctionDeclaration( - PatioFunctionDeclaration patioFunctionDeclaration, - LlvmPatioCodeGenModuleContext context - ) { - return context.addTopLevelEntity(new LlvmFunctionDeclaration( - patioFunctionDeclaration.name(), - patioTypeToLlvmType(patioFunctionDeclaration.returnType()), - patioFunctionDeclaration.params().stream() - .map(patioParam -> new LlvmParam( - patioTypeToLlvmType(patioParam.type()), - patioParam.name() - )) - .toList() - )); - } - - private static LlvmPatioCodeGenModuleContext generateFunctionDefinition( - PatioFunctionDefinition patioFunctionDefinition, - LlvmPatioCodeGenModuleContext moduleContext - ) { - var functionContext = moduleContext.enterFunction(); - - for (var patioLocal : patioFunctionDefinition.locals()) { - functionContext = functionContext.addPatioLocalToType( - patioLocal.ref(), - patioTypeToLlvmType(patioLocal.type()) - ); - } - for (var patioParam : patioFunctionDefinition.params()) { - // TODO: allocate LLVM name for params - functionContext = functionContext.addPatioLocal( - patioParam.ref(), - new LlvmVariableLocal(patioParam.name()) - ); - } - - functionContext = generateInstructions(patioFunctionDefinition.body(), functionContext); - - var llvmBodyResult = functionContext.toBasicBlocks(); - var llvmBody = llvmBodyResult.value(); - moduleContext = llvmBodyResult.context(); - - return moduleContext.addTopLevelEntity(new LlvmFunctionDefinition( - patioFunctionDefinition.name(), - patioTypeToLlvmType(patioFunctionDefinition.returnType()), - StreamSupport.stream(patioFunctionDefinition.params().spliterator(), false) - .map(patioParam -> new LlvmParam( - patioTypeToLlvmType(patioParam.type()), - patioParam.name() - )) - .toList(), - llvmBody - )); - } - - private static LlvmPatioCodeGenFunctionContext generateInstructions( - List patioInstructions, - LlvmPatioCodeGenFunctionContext context - ) { - for (var patioInstruction : patioInstructions) { - context = generateInstruction(patioInstruction, context); - } - - return context; - } - - private static LlvmPatioCodeGenFunctionContext generateInstruction( - PatioInstruction patioInstruction, - LlvmPatioCodeGenFunctionContext context - ) { - return switch (patioInstruction) { - case PatioAllocHeap patioAllocHeap -> { - var sizeResult = sizeOf(patioDataLayoutToLlvmType(patioAllocHeap.layout()), context); - var size = sizeResult.value(); - context = sizeResult.context(); - - var targetResult = context.addPatioLocalToLlvmLocal(patioAllocHeap.target()); - var target = targetResult.value(); - context = targetResult.context(); - - var offsetResult = offsetOf( - patioAllocHeap.layout(), - patioAllocHeap.gcChildrenOffset(), - context - ); - var offset = offsetResult.value(); - context = offsetResult.context(); - - yield context.addInstruction(ALLOC_DECLARATION.call( - Optional.of(target), - List.of( - size, - offset, - new LlvmOperandInt(patioAllocHeap.gcChildrenCount()) - ) - )); - } - - case PatioAllocStack patioAllocStack -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioAllocStack.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmAlloca( - target, - patioDataLayoutToLlvmType(patioAllocStack.layout()) - )); - } - - case PatioBlock patioBlock -> - generateInstructions(patioBlock.body(), context); - - case PatioBoolToInt patioBoolToInt -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioBoolToInt.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmZext( - target, - LlvmTypes.INT_1, - patioOperandToLlvmOperand(patioBoolToInt.value(), context), - LlvmTypes.INT_64 - )); - } - - case PatioCall patioCall -> { - var target = Optional.empty(); - if (patioCall.target().isPresent()) { - var targetResult = context.addPatioLocalToLlvmLocal(patioCall.target().get()); - target = Optional.of(targetResult.value()); - context = targetResult.context(); - } - - var c = context; - - yield context.addInstruction(new LlvmCall( - target, - context.functionNameToReturnType(patioCall.functionName()), - new LlvmVariableGlobal(patioCall.functionName()), - patioCall.args().stream() - .map(arg -> patioOperandToLlvmTypedOperand(arg, c)) - .toList() - )); - } - - case PatioCallPointer patioCallPointer -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioCallPointer.target()); - var target = targetResult.value(); - context = targetResult.context(); - - var c = context; - - yield context.addInstruction(new LlvmCall( - target, - context.patioLocalToType(patioCallPointer.target()), - patioOperandToLlvmOperand(patioCallPointer.functionPointer(), context), - patioCallPointer.args().stream() - .map(arg -> patioOperandToLlvmTypedOperand(arg, c)) - .toList() - )); - } - - case PatioDataPtrToInt patioDataPtrToInt -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioDataPtrToInt.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmPtrToInt( - target, - patioOperandToLlvmOperand(patioDataPtrToInt.value(), context), - LlvmTypes.INT_64 - )); - } - - case PatioIf patioIf -> { - var ifTrueStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.IF_TRUE_START - ); - var ifTrueStartLabel = ifTrueStartLabelResult.value(); - context = ifTrueStartLabelResult.context(); - - var ifTrueEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.IF_TRUE_END - ); - var ifTrueEndLabel = ifTrueEndLabelResult.value(); - context = ifTrueEndLabelResult.context(); - - var ifFalseStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.IF_FALSE_START - ); - var ifFalseStartLabel = ifFalseStartLabelResult.value(); - context = ifFalseStartLabelResult.context(); - - var ifFalseEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.IF_FALSE_END - ); - var ifFalseEndLabel = ifFalseEndLabelResult.value(); - context = ifFalseEndLabelResult.context(); - - var endLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.IF_END - ); - var endLabel = endLabelResult.value(); - context = endLabelResult.context(); - - context = context.addTerminatorInstruction(new LlvmBr( - patioOperandToLlvmOperand(patioIf.condition(), context), - ifTrueStartLabel, - ifFalseStartLabel - )); - - context = generateBranches( - patioIf.targets(), - List.of( - Branch.of(ifTrueStartLabel, ifTrueEndLabel, patioIf.trueBody()), - Branch.of(ifFalseStartLabel, ifFalseEndLabel, patioIf.falseBody()) - ), - endLabel, - context - ); - - yield context; - } - - case PatioIntAdd patioIntAdd -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioIntAdd.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmAdd( - target, - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioIntAdd.left(), context), - patioOperandToLlvmOperand(patioIntAdd.right(), context) - )); - } - - case PatioIntEquals patioIntEquals -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioIntEquals.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmIcmp( - target, - LlvmConditionCode.EQ, - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioIntEquals.left(), context), - patioOperandToLlvmOperand(patioIntEquals.right(), context) - )); - } - - case PatioIntNotEqual patioIntNotEqual -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioIntNotEqual.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmIcmp( - target, - LlvmConditionCode.NE, - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioIntNotEqual.left(), context), - patioOperandToLlvmOperand(patioIntNotEqual.right(), context) - )); - } - - case PatioIntSub patioIntSub -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioIntSub.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmSub( - target, - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioIntSub.left(), context), - patioOperandToLlvmOperand(patioIntSub.right(), context) - )); - } - - case PatioIntToBool patioIntToBool -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioIntToBool.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmTrunc( - target, - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioIntToBool.value(), context), - LlvmTypes.INT_1 - )); - } - - case PatioIntToDataPtr patioIntToDataPtr -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioIntToDataPtr.target()); - var target = targetResult.value(); - context = targetResult.context(); - - yield context.addInstruction(new LlvmIntToPtr( - target, - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioIntToDataPtr.value(), context) - )); - } - - case PatioLocalStore patioLocalStore -> - context.addPatioLocal( - patioLocalStore.target(), - patioOperandToLlvmOperand(patioLocalStore.value(), context) - ); - - case PatioLoop patioLoop -> { - var loopStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.LOOP_START - ); - var loopStartLabel = loopStartLabelResult.value(); - context = loopStartLabelResult.context(); - - var loopEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.LOOP_END - ); - var loopEndLabel = loopEndLabelResult.value(); - context = loopEndLabelResult.context(); - - context = context.startBasicBlock(loopStartLabel); - - context = context.enterLoop(loopStartLabel, loopEndLabel); - context = generateInstructions(patioLoop.body(), context); - context = context.exitLoop(); - - context = context.startBasicBlock(loopEndLabel); - - yield context; - } - - case PatioLoopBreak patioLoopBreak -> - context.addTerminatorInstruction(new LlvmBrUnconditional(context.loopEndLabel())); - - case PatioLoopContinue patioLoopContinue -> - context.addTerminatorInstruction(new LlvmBrUnconditional(context.loopStartLabel())); - - case PatioMemoryLoad patioMemoryLoad -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioMemoryLoad.target()); - var target = targetResult.value(); - context = targetResult.context(); - - var pointerResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TEMPORARY); - var pointer = pointerResult.value(); - context = pointerResult.context(); - - context = context.addInstruction(new LlvmGetElementPtr( - pointer, - LlvmTypes.pointer(patioDataLayoutToLlvmType(patioMemoryLoad.layout())), - patioOperandToLlvmOperand(patioMemoryLoad.pointer(), context), - patioDataOffsetsToLlvmOffsets(patioMemoryLoad.layout(), patioMemoryLoad.offset()) - )); - - context = context.addInstruction(new LlvmLoad( - target, - patioDataOffsetToLlvmType(patioMemoryLoad.layout(), patioMemoryLoad.offset()), - pointer - )); - - yield context; - } - - case PatioMemoryStore patioMemoryStore -> { - var pointerResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TEMPORARY); - var pointer = pointerResult.value(); - context = pointerResult.context(); - - context = context.addInstruction(new LlvmGetElementPtr( - pointer, - LlvmTypes.pointer(patioDataLayoutToLlvmType(patioMemoryStore.layout())), - patioOperandToLlvmOperand(patioMemoryStore.pointer(), context), - patioDataOffsetsToLlvmOffsets(patioMemoryStore.layout(), patioMemoryStore.offset()) - )); - - context = context.addInstruction(new LlvmStore( - patioDataOffsetToLlvmType(patioMemoryStore.layout(), patioMemoryStore.offset()), - patioOperandToLlvmOperand(patioMemoryStore.value(), context), - pointer - )); - - yield context; - } - - case PatioResume patioResume -> - context.addTerminatorInstruction(new LlvmReturn( - patioOperandToLlvmType(patioResume.value(), context), - patioOperandToLlvmOperand(patioResume.value(), context) - )); - - case PatioReturn patioReturn -> - context.addTerminatorInstruction(new LlvmReturn( - patioOperandToLlvmType(patioReturn.value(), context), - patioOperandToLlvmOperand(patioReturn.value(), context) - )); - - case PatioReturnVoid patioReturnVoid -> - context.addTerminatorInstruction(new LlvmReturnVoid()); - - case PatioSwitch patioSwitch -> { - var switchDefaultStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.SWITCH_DEFAULT_START - ); - var switchDefaultStartLabel = switchDefaultStartLabelResult.value(); - context = switchDefaultStartLabelResult.context(); - - var switchDefaultEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.SWITCH_DEFAULT_END - ); - var switchDefaultEndLabel = switchDefaultEndLabelResult.value(); - context = switchDefaultEndLabelResult.context(); - - var switchEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.SWITCH_END - ); - var switchEndLabel = switchEndLabelResult.value(); - context = switchEndLabelResult.context(); - - var llvmSwitchCases = new ArrayList(); - var branches = new ArrayList(); - for (var patioSwitchCase : patioSwitch.cases()) { - var caseStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.SWITCH_CASE_START, - Integer.toString(patioSwitchCase.value()) - ); - var caseStartLabel = caseStartLabelResult.value(); - context = caseStartLabelResult.context(); - - var caseEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.SWITCH_CASE_END, - Integer.toString(patioSwitchCase.value()) - ); - var caseEndLabel = caseEndLabelResult.value(); - context = caseEndLabelResult.context(); - - branches.add(Branch.of( - caseStartLabel, - caseEndLabel, - patioSwitchCase.body() - )); - - llvmSwitchCases.add(new LlvmSwitch.Case( - patioSwitchCase.value(), - caseStartLabel - )); - } - - branches.add(Branch.of( - switchDefaultStartLabel, - switchDefaultEndLabel, - patioSwitch.defaultBody() - )); - - context = context.addTerminatorInstruction(new LlvmSwitch( - LlvmTypes.INT_64, - patioOperandToLlvmOperand(patioSwitch.value(), context), - switchDefaultStartLabel, - llvmSwitchCases - )); - - context = generateBranches( - patioSwitch.targets(), - branches, - switchEndLabel, - context - ); - - yield context; - } - - case PatioThrow throw_ -> { - context = context.addInstruction(THROW_DECLARATION.call( - Optional.empty(), - List.of( - new LlvmOperandInt(context.exceptionTag(throw_.exceptionTypeName())), - patioOperandToLlvmOperand(throw_.value(), context) - ) - )); - - // TODO: construct a test for this in PatioExampleTests - context = context.addTerminatorInstruction(new LlvmUnreachable()); - - yield context; - } - - case PatioTry try_ -> { - var jmpBufResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.JMP_BUF); - var jmpBuf = jmpBufResult.value(); - context = jmpBufResult.context(); - - context = context.addInstruction(new LlvmAlloca( - jmpBuf, - jmpBufType() - )); - - var previousExceptionHandlerResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TRY_PREVIOUS_EXCEPTION_HANDLER); - var previousExceptionHandler = previousExceptionHandlerResult.value(); - context = previousExceptionHandlerResult.context(); - - context = context.addInstruction(new LlvmLoad( - previousExceptionHandler, - LlvmTypes.pointer(LlvmTypes.VOID), - new LlvmVariableGlobal(EXCEPTION_HANDLER_DECLARATION.name()) - )); - - var restorePreviousExceptionHandlerInstruction = new LlvmStore( - LlvmTypes.pointer(LlvmTypes.VOID), - previousExceptionHandler, - new LlvmVariableGlobal(EXCEPTION_HANDLER_DECLARATION.name()) - ); - - context = context.addInstruction(new LlvmStore( - LlvmTypes.pointer(LlvmTypes.VOID), - jmpBuf, - new LlvmVariableGlobal(EXCEPTION_HANDLER_DECLARATION.name()) - )); - - var setjmpValueResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.SETJMP_VALUE); - var setjmpValue = setjmpValueResult.value(); - context = setjmpValueResult.context(); - - context = context.addInstruction(SETJMP_DECLARATION.call( - Optional.of(setjmpValue), - List.of(jmpBuf) - )); - - var tryBodyStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.TRY_BODY_START - ); - var tryBodyStartLabel = tryBodyStartLabelResult.value(); - context = tryBodyStartLabelResult.context(); - - var tryBodyEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.TRY_BODY_END - ); - var tryBodyEndLabel = tryBodyEndLabelResult.value(); - context = tryBodyEndLabelResult.context(); - - var tryEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.TRY_END - ); - var tryEndLabel = tryEndLabelResult.value(); - context = tryEndLabelResult.context(); - - var branches = new ArrayList(); - var llvmSwitchCases = new ArrayList(); - - branches.add(new Branch( - tryBodyStartLabel, - tryBodyEndLabel, - List.of(), - try_.body(), - List.of(restorePreviousExceptionHandlerInstruction) - )); - llvmSwitchCases.add(new LlvmSwitch.Case(0, tryBodyStartLabel)); - - for (var catchClause : try_.catchClauses()) { - var catchBodyStartLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.TRY_CATCH_START - ); - var catchBodyStartLabel = catchBodyStartLabelResult.value(); - context = catchBodyStartLabelResult.context(); - - var catchBodyEndLabelResult = context.allocateLocalName( - LlvmCodeGenLocalVariableSpace.TRY_CATCH_END - ); - var catchBodyEndLabel = catchBodyEndLabelResult.value(); - context = catchBodyEndLabelResult.context(); - - var exceptionValueTargetResult = context.addPatioLocalToLlvmLocal(catchClause.target()); - var exceptionValueTarget = exceptionValueTargetResult.value(); - context = exceptionValueTargetResult.context(); - - branches.add(new Branch( - catchBodyStartLabel, - catchBodyEndLabel, - List.of( - restorePreviousExceptionHandlerInstruction, - new LlvmLoad( - exceptionValueTarget, - LlvmTypes.INT_64, - new LlvmVariableGlobal(EXCEPTION_VALUE_DECLARATION.name()) - ) - ), - catchClause.body(), - List.of() - )); - var tag = context.exceptionTag(catchClause.exceptionTypeName()); - llvmSwitchCases.add(new LlvmSwitch.Case(tag, catchBodyStartLabel)); - } - - var rethrowStartLabelResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TRY_RETHROW_START); - var rethrowStartLabel = rethrowStartLabelResult.value(); - context = rethrowStartLabelResult.context(); - - context = context.addTerminatorInstruction(new LlvmSwitch( - LlvmTypes.INT_32, - setjmpValue, - rethrowStartLabel, - llvmSwitchCases - )); - - context = context.startBasicBlock(rethrowStartLabel); - context = context.addInstruction(restorePreviousExceptionHandlerInstruction); - context = context.addInstruction(RETHROW_DECLARATION.call( - Optional.empty(), - List.of(setjmpValue) - )); - - context = generateBranches( - try_.targets(), - branches, - tryEndLabel, - context - ); - - yield context; - } - - case PatioUnreachable patioUnreachable -> - context.addTerminatorInstruction(new LlvmUnreachable()); - - case PatioVarLoad patioVarLoad -> { - var targetResult = context.addPatioLocalToLlvmLocal(patioVarLoad.target()); - var target = targetResult.value(); - context = targetResult.context(); - - context = context.addInstruction(new LlvmLoad( - target, - context.patioLocalToType(patioVarLoad.var()), - context.patioLocalToOperand(patioVarLoad.var()) - )); - - yield context; - } - - case PatioVarStore patioVarStore -> { - if (!context.patioLocalHasOperand(patioVarStore.var())) { - var varResult = context.addPatioLocalToLlvmLocal(patioVarStore.var()); - var var = varResult.value(); - context = varResult.context(); - - context = context.addInstruction(new LlvmAlloca( - var, - context.patioLocalToType(patioVarStore.var()) - )); - } - - context = context.addInstruction(new LlvmStore( - context.patioLocalToType(patioVarStore.var()), - patioOperandToLlvmOperand(patioVarStore.value(), context), - context.patioLocalToOperand(patioVarStore.var()) - )); - - yield context; - } - }; - } - - record Branch( - LlvmVariableLocal startLabel, - LlvmVariableLocal endLabel, - List prefixInstructions, - PatioBlock body, - List suffixInstructions - ) { - public static Branch of( - LlvmVariableLocal startLabel, - LlvmVariableLocal endLabel, - PatioBlock body - ) { - return new Branch(startLabel, endLabel, List.of(), body, List.of()); - } - } - - private static LlvmPatioCodeGenFunctionContext generateBranches( - List targets, - List branches, - LlvmVariableLocal endLabel, - LlvmPatioCodeGenFunctionContext context - ) { - var phiPredecessors = new ArrayList(); - for (var branch : branches) { - context = context.startBasicBlock(branch.startLabel); - - for (var prefixInstruction : branch.prefixInstructions) { - context = context.addInstruction(prefixInstruction); - } - - context = generateInstructions(branch.body.body(), context); - - for (var suffixInstruction : branch.suffixInstructions) { - context = context.addInstruction(suffixInstruction); - } - - var endIsUnreachable = branch.body.body().stream() - .anyMatch(instruction -> instruction.isBlockTerminator()); - - if (!endIsUnreachable) { - context = context.startBasicBlock(branch.endLabel); - context = context.addTerminatorInstruction(new LlvmBrUnconditional( - endLabel - )); - - phiPredecessors.add(new PhiPredecessor(branch.body, branch.endLabel)); - } - } - - context = context.startBasicBlock(endLabel); - context = generatePhiInstructions( - targets, - phiPredecessors, - context - ); - - return context; - } - - record PhiPredecessor(PatioBlock block, LlvmVariableLocal label) {} - - private static LlvmPatioCodeGenFunctionContext generatePhiInstructions( - List targets, - List predecessors, - LlvmPatioCodeGenFunctionContext context - ) { - for (var targetIndex = 0; targetIndex < targets.size(); targetIndex++) { - var patioTarget = targets.get(targetIndex); - - var llvmTargetResult = context.addPatioLocalToLlvmLocal(patioTarget); - var llvmTarget = llvmTargetResult.value(); - context = llvmTargetResult.context(); - - var blockTargetIndex = targetIndex; - var blockContext = context; - context = context.addInstruction(new LlvmPhi( - llvmTarget, - context.patioLocalToType(patioTarget), - predecessors.stream() - .map(predecessor -> new LlvmPhiPair( - patioOperandToLlvmOperand(predecessor.block.results().get(blockTargetIndex), blockContext), - predecessor.label - )) - .toList() - )); - } - - return context; - } - - private static PVector patioDataOffsetsToLlvmOffsets( - PatioDataLayout patioDataLayout, - List patioOffsets - ) { - return patioDataOffsetsToLlvmOffsets( - patioDataLayout, - TreePVector.from(patioOffsets), - P.vector() - ).plus(0, LlvmTypedOperand.arrayIndex(0)); - } - - private static PVector patioDataOffsetsToLlvmOffsets( - PatioDataLayout patioDataLayout, - PVector patioOffsets, - PVector llvmOffsets - ) { - if (patioOffsets.isEmpty()) { - return llvmOffsets; - } else { - var patioOffset = patioOffsets.get(0); - - return switch (patioDataLayout) { - case PatioDataLayoutArray patioDataLayoutArray -> - patioDataOffsetsToLlvmOffsets( - patioDataLayoutArray.element(), - patioOffsets.minus(0), - llvmOffsets.plus(LlvmTypedOperand.arrayIndex(patioOffset)) - ); - - case PatioDataLayoutBool patioDataLayoutBool -> - throw new RuntimeException("offset not allowed for bools"); - - case PatioDataLayoutByte patioDataLayoutByte -> - throw new RuntimeException("offset not allowed for bytes"); - - case PatioDataLayoutDataPointer patioDataLayoutdataPointer -> - throw new RuntimeException("offset not allowed for data ptrs"); - - case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> - throw new RuntimeException("offset not allowed for function ptrs"); - - case PatioDataLayoutInt patioDataLayoutInt -> - throw new RuntimeException("offset not allowed for ints"); - - case PatioDataLayoutStruct patioDataLayoutStruct -> - patioDataOffsetsToLlvmOffsets( - patioDataLayoutStruct.elements().get(patioOffset), - patioOffsets.minus(0), - llvmOffsets.plus(LlvmTypedOperand.structureIndex(patioOffset)) - ); - }; - } - } - - private static LlvmType patioDataOffsetToLlvmType( - PatioDataLayout patioDataLayout, - List patioOffsets - ) { - return patioDataOffsetToLlvmType(patioDataLayout, TreePVector.from(patioOffsets)); - } - - private static LlvmType patioDataOffsetToLlvmType( - PatioDataLayout patioDataLayout, - PVector patioOffsets - ) { - // TODO: check that offset is valid? - return switch (patioDataLayout) { - case PatioDataLayoutArray patioDataLayoutArray -> - patioDataOffsetToLlvmType( - patioDataLayoutArray.element(), - patioOffsets.minus(0) - ); - - case PatioDataLayoutBool patioDataLayoutBool -> - LlvmTypes.INT_1; - - case PatioDataLayoutByte patioDataLayoutByte -> - LlvmTypes.INT_8; - - case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioDataLayoutInt patioDataLayoutInt -> - LlvmTypes.INT_64; - - case PatioDataLayoutStruct patioDataLayoutStruct -> - patioDataOffsetToLlvmType( - patioDataLayoutStruct.elements().get(patioOffsets.get(0)), - patioOffsets.minus(0) - ); - }; - } - - private static LlvmTypedOperand patioOperandToLlvmTypedOperand( - PatioOperand patioOperand, - LlvmPatioCodeGenFunctionContext context - ) { - return new LlvmTypedOperand( - patioOperandToLlvmType(patioOperand, context), - patioOperandToLlvmOperand(patioOperand, context) - ); - } - - private static LlvmType patioOperandToLlvmType( - PatioOperand patioOperand, - LlvmPatioCodeGenFunctionContext context - ) { - return switch (patioOperand) { - case PatioBool patioBool -> - LlvmTypes.INT_1; - - case PatioDataPtr patioDataPtr -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioFunctionPtr patioFunctionPtr -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioInt patioInt -> - LlvmTypes.INT_64; - - case PatioLocalRef patioLocalRef -> - context.patioLocalToType(patioLocalRef); - }; - } - - private static LlvmOperand patioOperandToLlvmOperand( - PatioOperand patioOperand, - LlvmPatioCodeGenFunctionContext context - ) { - return switch (patioOperand) { - case PatioBool patioBool -> - new LlvmOperandInt(patioBool.value() ? 1 : 0); - - case PatioDataPtr patioDataPtr -> - new LlvmVariableGlobal(patioDataPtr.name()); - - case PatioFunctionPtr patioFunctionPtr -> - new LlvmVariableGlobal(patioFunctionPtr.name()); - - case PatioInt patioInt -> - new LlvmOperandInt(patioInt.value()); - - case PatioLocalRef patioLocalRef -> - context.patioLocalToOperand(patioLocalRef); - }; - } - - - private static LlvmType patioTypeToLlvmType(PatioType patioType) { - return switch (patioType) { - case PatioBoolType patioBoolType -> - LlvmTypes.INT_1; - - case PatioByteType patioByteType -> - LlvmTypes.INT_8; - - case PatioDataPointerType patioDataPointerType -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioFunctionPointerType patioFunctionPointerType -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioIntType patioIntType -> - LlvmTypes.INT_64; - - case PatioVoidType patioVoidType -> - LlvmTypes.VOID; - }; - } - - private static LlvmType patioDataLayoutToLlvmType(PatioDataLayout layout) { - return switch (layout) { - case PatioDataLayoutArray patioDataLayoutArray -> - LlvmTypes.array( - patioDataLayoutArray.size(), - patioDataLayoutToLlvmType(patioDataLayoutArray.element()) - ); - - case PatioDataLayoutBool patioDataLayoutBool -> - LlvmTypes.INT_1; - - case PatioDataLayoutByte patioDataLayoutByte -> - LlvmTypes.INT_8; - - case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> - LlvmTypes.pointer(LlvmTypes.VOID); - - case PatioDataLayoutInt patioDataLayoutInt -> - LlvmTypes.INT_64; - - case PatioDataLayoutStruct patioDataLayoutStruct -> - LlvmTypes.structure( - patioDataLayoutStruct.elements().stream() - .map(element -> patioDataLayoutToLlvmType(element)) - .toList() - ); - }; - } - - private static LlvmOperand patioDataLayoutToZeroedLlvmOperand(PatioDataLayout layout) { - return switch (layout) { - case PatioDataLayoutArray patioDataLayoutArray -> - new LlvmOperandArray( - patioDataLayoutToLlvmType(patioDataLayoutArray.element()), - Lists.repeat( - patioDataLayoutToZeroedLlvmOperand(patioDataLayoutArray.element()), - patioDataLayoutArray.size() - ) - ); - - case PatioDataLayoutBool patioDataLayoutBool -> - new LlvmOperandInt(0); - - case PatioDataLayoutByte patioDataLayoutByte -> - new LlvmOperandInt(0); - - case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> - new LlvmOperandNull(); - - case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> - new LlvmOperandNull(); - - case PatioDataLayoutInt patioDataLayoutInt -> - new LlvmOperandInt(0); - - case PatioDataLayoutStruct patioDataLayoutStruct -> - new LlvmOperandStructure( - patioDataLayoutStruct.elements().stream() - .map(element -> new LlvmTypedOperand( - patioDataLayoutToLlvmType(element), - patioDataLayoutToZeroedLlvmOperand(element) - )) - .toList() - ); - }; - } - - private static LlvmOperand patioDataValueToLlvmOperand(PatioDataValue value) { - return switch (value) { - case PatioDataValueArray patioDataValueArray -> { - var llvmType = patioDataLayoutToLlvmType(patioDataValueArray.elementLayout()); - - var llvmElements = patioDataValueArray.elements().stream() - .map(element -> patioDataValueToLlvmOperand(element)) - .toList(); - - yield new LlvmOperandArray(llvmType, llvmElements); - } - - case PatioDataValueBool patioDataValueByte -> - new LlvmOperandInt(patioDataValueByte.value() ? 1 : 0); - - case PatioDataValueByte patioDataValueByte -> - new LlvmOperandInt(patioDataValueByte.value()); - - case PatioDataValueDataPointerNull patioDataValueDataPointerNull -> - new LlvmOperandNull(); - - case PatioDataValueInt patioDataValueInt -> - new LlvmOperandInt(patioDataValueInt.value()); - - case PatioDataValueStruct patioDataValueStruct -> - new LlvmOperandStructure( - patioDataValueStruct.elements().stream() - .map(element -> new LlvmTypedOperand( - patioDataLayoutToLlvmType(element.layout()), - patioDataValueToLlvmOperand(element) - )) - .toList() - ); - }; - } - - public static LlvmPatioCodeGenFunctionContext.Result sizeOf( - LlvmType type, - LlvmPatioCodeGenFunctionContext context - ) { - var sizePtrResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.SIZE_PTR); - var sizePtr = sizePtrResult.value(); - context = sizePtrResult.context(); - - var sizeResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.SIZE); - var size = sizeResult.value(); - context = sizeResult.context(); - - return context - .addInstruction( - new LlvmGetElementPtr( - sizePtr, - LlvmTypes.pointer(type), - new LlvmOperandNull(), - List.of(LlvmTypedOperand.arrayIndex(1)) - ) - ) - .addInstruction( - new LlvmPtrToInt( - size, - sizePtr, - LlvmTypes.INT_64 - ) - ) - .toResult(size); - } - - private static LlvmPatioCodeGenFunctionContext.Result offsetOf( - PatioDataLayout patioDataLayout, - List patioOffset, - LlvmPatioCodeGenFunctionContext context - ) { - var offsetPtrResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.OFFSET_PTR); - var offsetPtr = offsetPtrResult.value(); - context = offsetPtrResult.context(); - - var offsetResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.OFFSET); - var offset = offsetResult.value(); - context = offsetResult.context(); - - return context - .addInstruction( - new LlvmGetElementPtr( - offsetPtr, - LlvmTypes.pointer(patioDataLayoutToLlvmType(patioDataLayout)), - new LlvmOperandNull(), - patioDataOffsetsToLlvmOffsets(patioDataLayout, patioOffset) - ) - ) - .addInstruction( - new LlvmPtrToInt( - offset, - offsetPtr, - LlvmTypes.INT_64 - ) - ) - .toResult(offset); - } -} diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/codegen/PatioToLlvm.java b/src/main/java/org/shedlang/compiler/backends/llvm/codegen/PatioToLlvm.java new file mode 100644 index 00000000..97b9dcc0 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/backends/llvm/codegen/PatioToLlvm.java @@ -0,0 +1,1289 @@ +package org.shedlang.compiler.backends.llvm.codegen; + +import org.pcollections.PVector; +import org.pcollections.TreePVector; +import org.shedlang.compiler.backends.llvm.LlvmCodeGenLocalVariableSpace; +import org.shedlang.compiler.backends.llvm.codegen.errors.SizeofJmpBufError; +import org.shedlang.compiler.backends.llvm.ir.ast.*; +import org.shedlang.compiler.backends.llvm.ir.ast.instructions.*; +import org.shedlang.compiler.backends.llvm.ir.ast.operands.*; +import org.shedlang.compiler.backends.llvm.ir.ast.types.LlvmType; +import org.shedlang.compiler.backends.llvm.ir.ast.types.LlvmTypes; +import org.shedlang.compiler.filesystem.ShedCompilerFiles; +import org.shedlang.compiler.patio.ast.*; +import org.shedlang.compiler.patio.ast.instructions.*; +import org.shedlang.compiler.patio.ast.operands.*; +import org.shedlang.compiler.patio.ast.types.*; +import org.shedlang.compiler.util.Lazy; +import org.shedlang.compiler.util.Lists; +import org.shedlang.compiler.util.P; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; + +public class PatioToLlvm { + private static final LlvmFunctionDeclaration ALLOC_DECLARATION = new LlvmFunctionDeclaration( + "shed__runtime__gc_alloc", + LlvmTypes.pointer(LlvmTypes.INT_8), + List.of( + new LlvmParam(LlvmTypes.INT_64, "size"), + new LlvmParam(LlvmTypes.INT_64, "children_offset"), + new LlvmParam(LlvmTypes.INT_64, "children_count") + ) + ); + + private static final LlvmType EXCEPTION_TAG_TYPE = LlvmTypes.INT_32; + private static final LlvmType EXCEPTION_VALUE_TYPE = LlvmTypes.INT_64; + + private static final Lazy JMP_BUF_SIZE = Lazy.of(() -> readJmpBufSize()); + + private static int readJmpBufSize() { + try { + var sizeofJmpBufString = Files.readString( + ShedCompilerFiles.getRoot().resolve("runtime/target/amd64/sizeof_jmp_buf.txt") + ); + return Integer.parseInt(sizeofJmpBufString.strip()); + } catch (IOException e) { + throw new SizeofJmpBufError(); + } + } + + private static LlvmType jmpBufType() { + return LlvmTypes.array(JMP_BUF_SIZE.get(), LlvmTypes.INT_8); + } + + private static final LlvmFunctionDeclaration SETJMP_DECLARATION = new LlvmFunctionDeclaration( + "_setjmp", + LlvmTypes.INT_32, + List.of( + new LlvmParam(LlvmTypes.pointer(LlvmTypes.VOID), "env") + ) + ); + + private static final LlvmGlobalDefinition EXCEPTION_HANDLER_DECLARATION = new LlvmGlobalDefinition( + "patio__runtime__exceptions_exception_handler", + LlvmTypes.pointer(LlvmTypes.VOID), + Optional.empty(), + false, + false, + false + ); + + private static final LlvmGlobalDefinition EXCEPTION_VALUE_DECLARATION = new LlvmGlobalDefinition( + "patio__runtime__exceptions_exception_value", + LlvmTypes.INT_64, + Optional.empty(), + false, + false, + false + ); + + private static final LlvmFunctionDeclaration THROW_DECLARATION = new LlvmFunctionDeclaration( + "patio__runtime__exceptions_throw", + LlvmTypes.pointer(LlvmTypes.VOID), + List.of( + new LlvmParam(EXCEPTION_TAG_TYPE, "exception_tag"), + new LlvmParam(EXCEPTION_VALUE_TYPE, "exception_value") + ) + ); + + private static final LlvmFunctionDeclaration RETHROW_DECLARATION = new LlvmFunctionDeclaration( + "patio__runtime__exceptions_rethrow", + LlvmTypes.pointer(LlvmTypes.VOID), + List.of( + new LlvmParam(EXCEPTION_TAG_TYPE, "exception_tag") + ) + ); + + public static LlvmPatioCodeGenGlobalContext.Result generateCompilationUnit( + PatioCompilationUnit patioCompilationUnit, + LlvmPatioCodeGenGlobalContext globalContext + ) { + var context = globalContext.enterModule() + .addTopLevelEntity(ALLOC_DECLARATION) + .addTopLevelEntity(SETJMP_DECLARATION) + .addTopLevelEntity(EXCEPTION_HANDLER_DECLARATION) + .addTopLevelEntity(EXCEPTION_VALUE_DECLARATION) + .addTopLevelEntity(THROW_DECLARATION) + .addTopLevelEntity(RETHROW_DECLARATION); + + for (var patioCompilationUnitElement : patioCompilationUnit.elements()) { + switch (patioCompilationUnitElement) { + case PatioDataElementImmutable patioDataElementImmutable -> { + } + case PatioDataElementMutable patioDataElementMutable -> { + } + case PatioFunctionDeclaration patioFunctionDeclaration -> { + context = context.addFunctionNameToReturnType( + patioFunctionDeclaration.name(), + patioTypeToLlvmType(patioFunctionDeclaration.returnType()) + ); + } + case PatioExceptionTypeDefinition patioExceptionTypeDefinition -> { + // TODO: this needs to be done across all compilation units to ensure + // consistency in tags and therefore allow cross-compilation unit throws. + + context = context.allocateExceptionTag(patioExceptionTypeDefinition.name()); + } + case PatioFunctionDefinition patioFunctionDefinition -> { + context = context.addFunctionNameToReturnType( + patioFunctionDefinition.name(), + patioTypeToLlvmType(patioFunctionDefinition.returnType()) + ); + } + } + } + + for (var patioCompilationUnitElement : patioCompilationUnit.elements()) { + context = generateCompilationUnitElement(patioCompilationUnitElement, context); + } + + return context.toModule(); + } + + private static LlvmPatioCodeGenModuleContext generateCompilationUnitElement( + PatioCompilationUnitElement patioCompilationUnitElement, + LlvmPatioCodeGenModuleContext context + ) { + return switch (patioCompilationUnitElement) { + case PatioDataElementImmutable patioDataElementImmutable -> + generateDataElementImmutable(patioDataElementImmutable, context); + + case PatioDataElementMutable patioDataElementMutable -> + generateDataElementMutable(patioDataElementMutable, context); + + case PatioExceptionTypeDefinition patioExceptionTypeDefinition -> + context; + + case PatioFunctionDeclaration patioFunctionDeclaration -> + generateFunctionDeclaration(patioFunctionDeclaration, context); + + case PatioFunctionDefinition patioFunctionDefinition -> + generateFunctionDefinition(patioFunctionDefinition, context); + }; + } + + private static LlvmPatioCodeGenModuleContext generateDataElementImmutable( + PatioDataElementImmutable patioDataElementImmutable, + LlvmPatioCodeGenModuleContext context + ) { + return context.addTopLevelEntity(new LlvmGlobalDefinition( + patioDataElementImmutable.name(), + patioDataLayoutToLlvmType(patioDataElementImmutable.value().layout()), + Optional.of(patioDataValueToLlvmOperand(patioDataElementImmutable.value())), + true, + true, + true + )); + } + + private static LlvmPatioCodeGenModuleContext generateDataElementMutable( + PatioDataElementMutable patioDataElementMutable, + LlvmPatioCodeGenModuleContext context + ) { + return context.addTopLevelEntity(new LlvmGlobalDefinition( + patioDataElementMutable.name(), + patioDataLayoutToLlvmType(patioDataElementMutable.initialValue().layout()), + Optional.of(patioDataValueToLlvmOperand(patioDataElementMutable.initialValue())), + false, + true, + true + )); + } + + private static LlvmPatioCodeGenModuleContext generateFunctionDeclaration( + PatioFunctionDeclaration patioFunctionDeclaration, + LlvmPatioCodeGenModuleContext context + ) { + return context.addTopLevelEntity(new LlvmFunctionDeclaration( + patioFunctionDeclaration.name(), + patioTypeToLlvmType(patioFunctionDeclaration.returnType()), + patioFunctionDeclaration.params().stream() + .map(patioParam -> new LlvmParam( + patioTypeToLlvmType(patioParam.type()), + patioParam.name() + )) + .toList() + )); + } + + private static LlvmPatioCodeGenModuleContext generateFunctionDefinition( + PatioFunctionDefinition patioFunctionDefinition, + LlvmPatioCodeGenModuleContext moduleContext + ) { + var functionContext = moduleContext.enterFunction(); + + for (var patioLocal : patioFunctionDefinition.locals()) { + functionContext = functionContext.addPatioLocalToType( + patioLocal.ref(), + patioTypeToLlvmType(patioLocal.type()) + ); + } + for (var patioParam : patioFunctionDefinition.params()) { + // TODO: allocate LLVM name for params + functionContext = functionContext.addPatioLocal( + patioParam.ref(), + new LlvmVariableLocal(patioParam.name()) + ); + } + + functionContext = generateInstructions(patioFunctionDefinition.body(), functionContext); + + var llvmBodyResult = functionContext.toBasicBlocks(); + var llvmBody = llvmBodyResult.value(); + moduleContext = llvmBodyResult.context(); + + return moduleContext.addTopLevelEntity(new LlvmFunctionDefinition( + patioFunctionDefinition.name(), + patioTypeToLlvmType(patioFunctionDefinition.returnType()), + StreamSupport.stream(patioFunctionDefinition.params().spliterator(), false) + .map(patioParam -> new LlvmParam( + patioTypeToLlvmType(patioParam.type()), + patioParam.name() + )) + .toList(), + llvmBody + )); + } + + private static LlvmPatioCodeGenFunctionContext generateInstructions( + List patioInstructions, + LlvmPatioCodeGenFunctionContext context + ) { + for (var patioInstruction : patioInstructions) { + context = generateInstruction(patioInstruction, context); + } + + return context; + } + + private static LlvmPatioCodeGenFunctionContext generateInstruction( + PatioInstruction patioInstruction, + LlvmPatioCodeGenFunctionContext context + ) { + return switch (patioInstruction) { + case PatioAllocHeap patioAllocHeap -> { + var sizeResult = sizeOf(patioDataLayoutToLlvmType(patioAllocHeap.layout()), context); + var size = sizeResult.value(); + context = sizeResult.context(); + + var targetResult = context.addPatioLocalToLlvmLocal(patioAllocHeap.target()); + var target = targetResult.value(); + context = targetResult.context(); + + var offsetResult = offsetOf( + patioAllocHeap.layout(), + patioAllocHeap.gcChildrenOffset(), + context + ); + var offset = offsetResult.value(); + context = offsetResult.context(); + + yield context.addInstruction(ALLOC_DECLARATION.call( + Optional.of(target), + List.of( + size, + offset, + new LlvmOperandInt(patioAllocHeap.gcChildrenCount()) + ) + )); + } + + case PatioAllocStack patioAllocStack -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioAllocStack.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmAlloca( + target, + patioDataLayoutToLlvmType(patioAllocStack.layout()) + )); + } + + case PatioBlock patioBlock -> + generateInstructions(patioBlock.body(), context); + + case PatioBoolToInt patioBoolToInt -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioBoolToInt.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmZext( + target, + LlvmTypes.INT_1, + patioOperandToLlvmOperand(patioBoolToInt.value(), context), + LlvmTypes.INT_64 + )); + } + + case PatioCall patioCall -> { + var target = Optional.empty(); + if (patioCall.target().isPresent()) { + var targetResult = context.addPatioLocalToLlvmLocal(patioCall.target().get()); + target = Optional.of(targetResult.value()); + context = targetResult.context(); + } + + var c = context; + + yield context.addInstruction(new LlvmCall( + target, + context.functionNameToReturnType(patioCall.functionName()), + new LlvmVariableGlobal(patioCall.functionName()), + patioCall.args().stream() + .map(arg -> patioOperandToLlvmTypedOperand(arg, c)) + .toList() + )); + } + + case PatioCallPointer patioCallPointer -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioCallPointer.target()); + var target = targetResult.value(); + context = targetResult.context(); + + var c = context; + + yield context.addInstruction(new LlvmCall( + target, + context.patioLocalToType(patioCallPointer.target()), + patioOperandToLlvmOperand(patioCallPointer.functionPointer(), context), + patioCallPointer.args().stream() + .map(arg -> patioOperandToLlvmTypedOperand(arg, c)) + .toList() + )); + } + + case PatioDataPtrToInt patioDataPtrToInt -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioDataPtrToInt.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmPtrToInt( + target, + patioOperandToLlvmOperand(patioDataPtrToInt.value(), context), + LlvmTypes.INT_64 + )); + } + + case PatioIf patioIf -> { + var ifTrueStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.IF_TRUE_START + ); + var ifTrueStartLabel = ifTrueStartLabelResult.value(); + context = ifTrueStartLabelResult.context(); + + var ifTrueEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.IF_TRUE_END + ); + var ifTrueEndLabel = ifTrueEndLabelResult.value(); + context = ifTrueEndLabelResult.context(); + + var ifFalseStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.IF_FALSE_START + ); + var ifFalseStartLabel = ifFalseStartLabelResult.value(); + context = ifFalseStartLabelResult.context(); + + var ifFalseEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.IF_FALSE_END + ); + var ifFalseEndLabel = ifFalseEndLabelResult.value(); + context = ifFalseEndLabelResult.context(); + + var endLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.IF_END + ); + var endLabel = endLabelResult.value(); + context = endLabelResult.context(); + + context = context.addTerminatorInstruction(new LlvmBr( + patioOperandToLlvmOperand(patioIf.condition(), context), + ifTrueStartLabel, + ifFalseStartLabel + )); + + context = generateBranches( + patioIf.targets(), + List.of( + Branch.of(ifTrueStartLabel, ifTrueEndLabel, patioIf.trueBody()), + Branch.of(ifFalseStartLabel, ifFalseEndLabel, patioIf.falseBody()) + ), + endLabel, + context + ); + + yield context; + } + + case PatioIntAdd patioIntAdd -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioIntAdd.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmAdd( + target, + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioIntAdd.left(), context), + patioOperandToLlvmOperand(patioIntAdd.right(), context) + )); + } + + case PatioIntEquals patioIntEquals -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioIntEquals.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmIcmp( + target, + LlvmConditionCode.EQ, + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioIntEquals.left(), context), + patioOperandToLlvmOperand(patioIntEquals.right(), context) + )); + } + + case PatioIntNotEqual patioIntNotEqual -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioIntNotEqual.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmIcmp( + target, + LlvmConditionCode.NE, + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioIntNotEqual.left(), context), + patioOperandToLlvmOperand(patioIntNotEqual.right(), context) + )); + } + + case PatioIntSub patioIntSub -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioIntSub.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmSub( + target, + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioIntSub.left(), context), + patioOperandToLlvmOperand(patioIntSub.right(), context) + )); + } + + case PatioIntToBool patioIntToBool -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioIntToBool.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmTrunc( + target, + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioIntToBool.value(), context), + LlvmTypes.INT_1 + )); + } + + case PatioIntToDataPtr patioIntToDataPtr -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioIntToDataPtr.target()); + var target = targetResult.value(); + context = targetResult.context(); + + yield context.addInstruction(new LlvmIntToPtr( + target, + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioIntToDataPtr.value(), context) + )); + } + + case PatioLocalStore patioLocalStore -> + context.addPatioLocal( + patioLocalStore.target(), + patioOperandToLlvmOperand(patioLocalStore.value(), context) + ); + + case PatioLoop patioLoop -> { + var loopStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.LOOP_START + ); + var loopStartLabel = loopStartLabelResult.value(); + context = loopStartLabelResult.context(); + + var loopEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.LOOP_END + ); + var loopEndLabel = loopEndLabelResult.value(); + context = loopEndLabelResult.context(); + + context = context.startBasicBlock(loopStartLabel); + + context = context.enterLoop(loopStartLabel, loopEndLabel); + context = generateInstructions(patioLoop.body(), context); + context = context.exitLoop(); + + context = context.startBasicBlock(loopEndLabel); + + yield context; + } + + case PatioLoopBreak patioLoopBreak -> + context.addTerminatorInstruction(new LlvmBrUnconditional(context.loopEndLabel())); + + case PatioLoopContinue patioLoopContinue -> + context.addTerminatorInstruction(new LlvmBrUnconditional(context.loopStartLabel())); + + case PatioMemoryLoad patioMemoryLoad -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioMemoryLoad.target()); + var target = targetResult.value(); + context = targetResult.context(); + + var pointerResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TEMPORARY); + var pointer = pointerResult.value(); + context = pointerResult.context(); + + context = context.addInstruction(new LlvmGetElementPtr( + pointer, + LlvmTypes.pointer(patioDataLayoutToLlvmType(patioMemoryLoad.layout())), + patioOperandToLlvmOperand(patioMemoryLoad.pointer(), context), + patioDataOffsetsToLlvmOffsets(patioMemoryLoad.layout(), patioMemoryLoad.offset()) + )); + + context = context.addInstruction(new LlvmLoad( + target, + patioDataOffsetToLlvmType(patioMemoryLoad.layout(), patioMemoryLoad.offset()), + pointer + )); + + yield context; + } + + case PatioMemoryStore patioMemoryStore -> { + var pointerResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TEMPORARY); + var pointer = pointerResult.value(); + context = pointerResult.context(); + + context = context.addInstruction(new LlvmGetElementPtr( + pointer, + LlvmTypes.pointer(patioDataLayoutToLlvmType(patioMemoryStore.layout())), + patioOperandToLlvmOperand(patioMemoryStore.pointer(), context), + patioDataOffsetsToLlvmOffsets(patioMemoryStore.layout(), patioMemoryStore.offset()) + )); + + context = context.addInstruction(new LlvmStore( + patioDataOffsetToLlvmType(patioMemoryStore.layout(), patioMemoryStore.offset()), + patioOperandToLlvmOperand(patioMemoryStore.value(), context), + pointer + )); + + yield context; + } + + case PatioResume patioResume -> + context.addTerminatorInstruction(new LlvmReturn( + patioOperandToLlvmType(patioResume.value(), context), + patioOperandToLlvmOperand(patioResume.value(), context) + )); + + case PatioReturn patioReturn -> + context.addTerminatorInstruction(new LlvmReturn( + patioOperandToLlvmType(patioReturn.value(), context), + patioOperandToLlvmOperand(patioReturn.value(), context) + )); + + case PatioReturnVoid patioReturnVoid -> + context.addTerminatorInstruction(new LlvmReturnVoid()); + + case PatioSwitch patioSwitch -> { + var switchDefaultStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.SWITCH_DEFAULT_START + ); + var switchDefaultStartLabel = switchDefaultStartLabelResult.value(); + context = switchDefaultStartLabelResult.context(); + + var switchDefaultEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.SWITCH_DEFAULT_END + ); + var switchDefaultEndLabel = switchDefaultEndLabelResult.value(); + context = switchDefaultEndLabelResult.context(); + + var switchEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.SWITCH_END + ); + var switchEndLabel = switchEndLabelResult.value(); + context = switchEndLabelResult.context(); + + var llvmSwitchCases = new ArrayList(); + var branches = new ArrayList(); + for (var patioSwitchCase : patioSwitch.cases()) { + var caseStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.SWITCH_CASE_START, + Integer.toString(patioSwitchCase.value()) + ); + var caseStartLabel = caseStartLabelResult.value(); + context = caseStartLabelResult.context(); + + var caseEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.SWITCH_CASE_END, + Integer.toString(patioSwitchCase.value()) + ); + var caseEndLabel = caseEndLabelResult.value(); + context = caseEndLabelResult.context(); + + branches.add(Branch.of( + caseStartLabel, + caseEndLabel, + patioSwitchCase.body() + )); + + llvmSwitchCases.add(new LlvmSwitch.Case( + patioSwitchCase.value(), + caseStartLabel + )); + } + + branches.add(Branch.of( + switchDefaultStartLabel, + switchDefaultEndLabel, + patioSwitch.defaultBody() + )); + + context = context.addTerminatorInstruction(new LlvmSwitch( + LlvmTypes.INT_64, + patioOperandToLlvmOperand(patioSwitch.value(), context), + switchDefaultStartLabel, + llvmSwitchCases + )); + + context = generateBranches( + patioSwitch.targets(), + branches, + switchEndLabel, + context + ); + + yield context; + } + + case PatioThrow throw_ -> { + context = context.addInstruction(THROW_DECLARATION.call( + Optional.empty(), + List.of( + new LlvmOperandInt(context.exceptionTag(throw_.exceptionTypeName())), + patioOperandToLlvmOperand(throw_.value(), context) + ) + )); + + // TODO: construct a test for this in PatioExampleTests + context = context.addTerminatorInstruction(new LlvmUnreachable()); + + yield context; + } + + case PatioTry try_ -> { + var jmpBufResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.JMP_BUF); + var jmpBuf = jmpBufResult.value(); + context = jmpBufResult.context(); + + context = context.addInstruction(new LlvmAlloca( + jmpBuf, + jmpBufType() + )); + + var previousExceptionHandlerResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TRY_PREVIOUS_EXCEPTION_HANDLER); + var previousExceptionHandler = previousExceptionHandlerResult.value(); + context = previousExceptionHandlerResult.context(); + + context = context.addInstruction(new LlvmLoad( + previousExceptionHandler, + LlvmTypes.pointer(LlvmTypes.VOID), + new LlvmVariableGlobal(EXCEPTION_HANDLER_DECLARATION.name()) + )); + + var restorePreviousExceptionHandlerInstruction = new LlvmStore( + LlvmTypes.pointer(LlvmTypes.VOID), + previousExceptionHandler, + new LlvmVariableGlobal(EXCEPTION_HANDLER_DECLARATION.name()) + ); + + context = context.addInstruction(new LlvmStore( + LlvmTypes.pointer(LlvmTypes.VOID), + jmpBuf, + new LlvmVariableGlobal(EXCEPTION_HANDLER_DECLARATION.name()) + )); + + var setjmpValueResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.SETJMP_VALUE); + var setjmpValue = setjmpValueResult.value(); + context = setjmpValueResult.context(); + + context = context.addInstruction(SETJMP_DECLARATION.call( + Optional.of(setjmpValue), + List.of(jmpBuf) + )); + + var tryBodyStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.TRY_BODY_START + ); + var tryBodyStartLabel = tryBodyStartLabelResult.value(); + context = tryBodyStartLabelResult.context(); + + var tryBodyEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.TRY_BODY_END + ); + var tryBodyEndLabel = tryBodyEndLabelResult.value(); + context = tryBodyEndLabelResult.context(); + + var tryEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.TRY_END + ); + var tryEndLabel = tryEndLabelResult.value(); + context = tryEndLabelResult.context(); + + var branches = new ArrayList(); + var llvmSwitchCases = new ArrayList(); + + branches.add(new Branch( + tryBodyStartLabel, + tryBodyEndLabel, + List.of(), + try_.body(), + List.of(restorePreviousExceptionHandlerInstruction) + )); + llvmSwitchCases.add(new LlvmSwitch.Case(0, tryBodyStartLabel)); + + for (var catchClause : try_.catchClauses()) { + var catchBodyStartLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.TRY_CATCH_START + ); + var catchBodyStartLabel = catchBodyStartLabelResult.value(); + context = catchBodyStartLabelResult.context(); + + var catchBodyEndLabelResult = context.allocateLocalName( + LlvmCodeGenLocalVariableSpace.TRY_CATCH_END + ); + var catchBodyEndLabel = catchBodyEndLabelResult.value(); + context = catchBodyEndLabelResult.context(); + + var exceptionValueTargetResult = context.addPatioLocalToLlvmLocal(catchClause.target()); + var exceptionValueTarget = exceptionValueTargetResult.value(); + context = exceptionValueTargetResult.context(); + + branches.add(new Branch( + catchBodyStartLabel, + catchBodyEndLabel, + List.of( + restorePreviousExceptionHandlerInstruction, + new LlvmLoad( + exceptionValueTarget, + LlvmTypes.INT_64, + new LlvmVariableGlobal(EXCEPTION_VALUE_DECLARATION.name()) + ) + ), + catchClause.body(), + List.of() + )); + var tag = context.exceptionTag(catchClause.exceptionTypeName()); + llvmSwitchCases.add(new LlvmSwitch.Case(tag, catchBodyStartLabel)); + } + + var rethrowStartLabelResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.TRY_RETHROW_START); + var rethrowStartLabel = rethrowStartLabelResult.value(); + context = rethrowStartLabelResult.context(); + + context = context.addTerminatorInstruction(new LlvmSwitch( + LlvmTypes.INT_32, + setjmpValue, + rethrowStartLabel, + llvmSwitchCases + )); + + context = context.startBasicBlock(rethrowStartLabel); + context = context.addInstruction(restorePreviousExceptionHandlerInstruction); + context = context.addInstruction(RETHROW_DECLARATION.call( + Optional.empty(), + List.of(setjmpValue) + )); + + context = generateBranches( + try_.targets(), + branches, + tryEndLabel, + context + ); + + yield context; + } + + case PatioUnreachable patioUnreachable -> + context.addTerminatorInstruction(new LlvmUnreachable()); + + case PatioVarLoad patioVarLoad -> { + var targetResult = context.addPatioLocalToLlvmLocal(patioVarLoad.target()); + var target = targetResult.value(); + context = targetResult.context(); + + context = context.addInstruction(new LlvmLoad( + target, + context.patioLocalToType(patioVarLoad.var()), + context.patioLocalToOperand(patioVarLoad.var()) + )); + + yield context; + } + + case PatioVarStore patioVarStore -> { + if (!context.patioLocalHasOperand(patioVarStore.var())) { + var varResult = context.addPatioLocalToLlvmLocal(patioVarStore.var()); + var var = varResult.value(); + context = varResult.context(); + + context = context.addInstruction(new LlvmAlloca( + var, + context.patioLocalToType(patioVarStore.var()) + )); + } + + context = context.addInstruction(new LlvmStore( + context.patioLocalToType(patioVarStore.var()), + patioOperandToLlvmOperand(patioVarStore.value(), context), + context.patioLocalToOperand(patioVarStore.var()) + )); + + yield context; + } + }; + } + + record Branch( + LlvmVariableLocal startLabel, + LlvmVariableLocal endLabel, + List prefixInstructions, + PatioBlock body, + List suffixInstructions + ) { + public static Branch of( + LlvmVariableLocal startLabel, + LlvmVariableLocal endLabel, + PatioBlock body + ) { + return new Branch(startLabel, endLabel, List.of(), body, List.of()); + } + } + + private static LlvmPatioCodeGenFunctionContext generateBranches( + List targets, + List branches, + LlvmVariableLocal endLabel, + LlvmPatioCodeGenFunctionContext context + ) { + var phiPredecessors = new ArrayList(); + for (var branch : branches) { + context = context.startBasicBlock(branch.startLabel); + + for (var prefixInstruction : branch.prefixInstructions) { + context = context.addInstruction(prefixInstruction); + } + + context = generateInstructions(branch.body.body(), context); + + for (var suffixInstruction : branch.suffixInstructions) { + context = context.addInstruction(suffixInstruction); + } + + var endIsUnreachable = branch.body.body().stream() + .anyMatch(instruction -> instruction.isBlockTerminator()); + + if (!endIsUnreachable) { + context = context.startBasicBlock(branch.endLabel); + context = context.addTerminatorInstruction(new LlvmBrUnconditional( + endLabel + )); + + phiPredecessors.add(new PhiPredecessor(branch.body, branch.endLabel)); + } + } + + context = context.startBasicBlock(endLabel); + context = generatePhiInstructions( + targets, + phiPredecessors, + context + ); + + return context; + } + + record PhiPredecessor(PatioBlock block, LlvmVariableLocal label) {} + + private static LlvmPatioCodeGenFunctionContext generatePhiInstructions( + List targets, + List predecessors, + LlvmPatioCodeGenFunctionContext context + ) { + for (var targetIndex = 0; targetIndex < targets.size(); targetIndex++) { + var patioTarget = targets.get(targetIndex); + + var llvmTargetResult = context.addPatioLocalToLlvmLocal(patioTarget); + var llvmTarget = llvmTargetResult.value(); + context = llvmTargetResult.context(); + + var blockTargetIndex = targetIndex; + var blockContext = context; + context = context.addInstruction(new LlvmPhi( + llvmTarget, + context.patioLocalToType(patioTarget), + predecessors.stream() + .map(predecessor -> new LlvmPhiPair( + patioOperandToLlvmOperand(predecessor.block.results().get(blockTargetIndex), blockContext), + predecessor.label + )) + .toList() + )); + } + + return context; + } + + private static PVector patioDataOffsetsToLlvmOffsets( + PatioDataLayout patioDataLayout, + List patioOffsets + ) { + return patioDataOffsetsToLlvmOffsets( + patioDataLayout, + TreePVector.from(patioOffsets), + P.vector() + ).plus(0, LlvmTypedOperand.arrayIndex(0)); + } + + private static PVector patioDataOffsetsToLlvmOffsets( + PatioDataLayout patioDataLayout, + PVector patioOffsets, + PVector llvmOffsets + ) { + if (patioOffsets.isEmpty()) { + return llvmOffsets; + } else { + var patioOffset = patioOffsets.get(0); + + return switch (patioDataLayout) { + case PatioDataLayoutArray patioDataLayoutArray -> + patioDataOffsetsToLlvmOffsets( + patioDataLayoutArray.element(), + patioOffsets.minus(0), + llvmOffsets.plus(LlvmTypedOperand.arrayIndex(patioOffset)) + ); + + case PatioDataLayoutBool patioDataLayoutBool -> + throw new RuntimeException("offset not allowed for bools"); + + case PatioDataLayoutByte patioDataLayoutByte -> + throw new RuntimeException("offset not allowed for bytes"); + + case PatioDataLayoutDataPointer patioDataLayoutdataPointer -> + throw new RuntimeException("offset not allowed for data ptrs"); + + case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> + throw new RuntimeException("offset not allowed for function ptrs"); + + case PatioDataLayoutInt patioDataLayoutInt -> + throw new RuntimeException("offset not allowed for ints"); + + case PatioDataLayoutStruct patioDataLayoutStruct -> + patioDataOffsetsToLlvmOffsets( + patioDataLayoutStruct.elements().get(patioOffset), + patioOffsets.minus(0), + llvmOffsets.plus(LlvmTypedOperand.structureIndex(patioOffset)) + ); + }; + } + } + + private static LlvmType patioDataOffsetToLlvmType( + PatioDataLayout patioDataLayout, + List patioOffsets + ) { + return patioDataOffsetToLlvmType(patioDataLayout, TreePVector.from(patioOffsets)); + } + + private static LlvmType patioDataOffsetToLlvmType( + PatioDataLayout patioDataLayout, + PVector patioOffsets + ) { + // TODO: check that offset is valid? + return switch (patioDataLayout) { + case PatioDataLayoutArray patioDataLayoutArray -> + patioDataOffsetToLlvmType( + patioDataLayoutArray.element(), + patioOffsets.minus(0) + ); + + case PatioDataLayoutBool patioDataLayoutBool -> + LlvmTypes.INT_1; + + case PatioDataLayoutByte patioDataLayoutByte -> + LlvmTypes.INT_8; + + case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioDataLayoutInt patioDataLayoutInt -> + LlvmTypes.INT_64; + + case PatioDataLayoutStruct patioDataLayoutStruct -> + patioDataOffsetToLlvmType( + patioDataLayoutStruct.elements().get(patioOffsets.get(0)), + patioOffsets.minus(0) + ); + }; + } + + private static LlvmTypedOperand patioOperandToLlvmTypedOperand( + PatioOperand patioOperand, + LlvmPatioCodeGenFunctionContext context + ) { + return new LlvmTypedOperand( + patioOperandToLlvmType(patioOperand, context), + patioOperandToLlvmOperand(patioOperand, context) + ); + } + + private static LlvmType patioOperandToLlvmType( + PatioOperand patioOperand, + LlvmPatioCodeGenFunctionContext context + ) { + return switch (patioOperand) { + case PatioBool patioBool -> + LlvmTypes.INT_1; + + case PatioDataPtr patioDataPtr -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioFunctionPtr patioFunctionPtr -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioInt patioInt -> + LlvmTypes.INT_64; + + case PatioLocalRef patioLocalRef -> + context.patioLocalToType(patioLocalRef); + }; + } + + private static LlvmOperand patioOperandToLlvmOperand( + PatioOperand patioOperand, + LlvmPatioCodeGenFunctionContext context + ) { + return switch (patioOperand) { + case PatioBool patioBool -> + new LlvmOperandInt(patioBool.value() ? 1 : 0); + + case PatioDataPtr patioDataPtr -> + new LlvmVariableGlobal(patioDataPtr.name()); + + case PatioFunctionPtr patioFunctionPtr -> + new LlvmVariableGlobal(patioFunctionPtr.name()); + + case PatioInt patioInt -> + new LlvmOperandInt(patioInt.value()); + + case PatioLocalRef patioLocalRef -> + context.patioLocalToOperand(patioLocalRef); + }; + } + + + private static LlvmType patioTypeToLlvmType(PatioType patioType) { + return switch (patioType) { + case PatioBoolType patioBoolType -> + LlvmTypes.INT_1; + + case PatioByteType patioByteType -> + LlvmTypes.INT_8; + + case PatioDataPointerType patioDataPointerType -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioFunctionPointerType patioFunctionPointerType -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioIntType patioIntType -> + LlvmTypes.INT_64; + + case PatioVoidType patioVoidType -> + LlvmTypes.VOID; + }; + } + + private static LlvmType patioDataLayoutToLlvmType(PatioDataLayout layout) { + return switch (layout) { + case PatioDataLayoutArray patioDataLayoutArray -> + LlvmTypes.array( + patioDataLayoutArray.size(), + patioDataLayoutToLlvmType(patioDataLayoutArray.element()) + ); + + case PatioDataLayoutBool patioDataLayoutBool -> + LlvmTypes.INT_1; + + case PatioDataLayoutByte patioDataLayoutByte -> + LlvmTypes.INT_8; + + case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> + LlvmTypes.pointer(LlvmTypes.VOID); + + case PatioDataLayoutInt patioDataLayoutInt -> + LlvmTypes.INT_64; + + case PatioDataLayoutStruct patioDataLayoutStruct -> + LlvmTypes.structure( + patioDataLayoutStruct.elements().stream() + .map(element -> patioDataLayoutToLlvmType(element)) + .toList() + ); + }; + } + + private static LlvmOperand patioDataLayoutToZeroedLlvmOperand(PatioDataLayout layout) { + return switch (layout) { + case PatioDataLayoutArray patioDataLayoutArray -> + new LlvmOperandArray( + patioDataLayoutToLlvmType(patioDataLayoutArray.element()), + Lists.repeat( + patioDataLayoutToZeroedLlvmOperand(patioDataLayoutArray.element()), + patioDataLayoutArray.size() + ) + ); + + case PatioDataLayoutBool patioDataLayoutBool -> + new LlvmOperandInt(0); + + case PatioDataLayoutByte patioDataLayoutByte -> + new LlvmOperandInt(0); + + case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> + new LlvmOperandNull(); + + case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> + new LlvmOperandNull(); + + case PatioDataLayoutInt patioDataLayoutInt -> + new LlvmOperandInt(0); + + case PatioDataLayoutStruct patioDataLayoutStruct -> + new LlvmOperandStructure( + patioDataLayoutStruct.elements().stream() + .map(element -> new LlvmTypedOperand( + patioDataLayoutToLlvmType(element), + patioDataLayoutToZeroedLlvmOperand(element) + )) + .toList() + ); + }; + } + + private static LlvmOperand patioDataValueToLlvmOperand(PatioDataValue value) { + return switch (value) { + case PatioDataValueArray patioDataValueArray -> { + var llvmType = patioDataLayoutToLlvmType(patioDataValueArray.elementLayout()); + + var llvmElements = patioDataValueArray.elements().stream() + .map(element -> patioDataValueToLlvmOperand(element)) + .toList(); + + yield new LlvmOperandArray(llvmType, llvmElements); + } + + case PatioDataValueBool patioDataValueByte -> + new LlvmOperandInt(patioDataValueByte.value() ? 1 : 0); + + case PatioDataValueByte patioDataValueByte -> + new LlvmOperandInt(patioDataValueByte.value()); + + case PatioDataValueDataPointerNull patioDataValueDataPointerNull -> + new LlvmOperandNull(); + + case PatioDataValueInt patioDataValueInt -> + new LlvmOperandInt(patioDataValueInt.value()); + + case PatioDataValueStruct patioDataValueStruct -> + new LlvmOperandStructure( + patioDataValueStruct.elements().stream() + .map(element -> new LlvmTypedOperand( + patioDataLayoutToLlvmType(element.layout()), + patioDataValueToLlvmOperand(element) + )) + .toList() + ); + }; + } + + public static LlvmPatioCodeGenFunctionContext.Result sizeOf( + LlvmType type, + LlvmPatioCodeGenFunctionContext context + ) { + var sizePtrResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.SIZE_PTR); + var sizePtr = sizePtrResult.value(); + context = sizePtrResult.context(); + + var sizeResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.SIZE); + var size = sizeResult.value(); + context = sizeResult.context(); + + return context + .addInstruction( + new LlvmGetElementPtr( + sizePtr, + LlvmTypes.pointer(type), + new LlvmOperandNull(), + List.of(LlvmTypedOperand.arrayIndex(1)) + ) + ) + .addInstruction( + new LlvmPtrToInt( + size, + sizePtr, + LlvmTypes.INT_64 + ) + ) + .toResult(size); + } + + private static LlvmPatioCodeGenFunctionContext.Result offsetOf( + PatioDataLayout patioDataLayout, + List patioOffset, + LlvmPatioCodeGenFunctionContext context + ) { + var offsetPtrResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.OFFSET_PTR); + var offsetPtr = offsetPtrResult.value(); + context = offsetPtrResult.context(); + + var offsetResult = context.allocateLocalName(LlvmCodeGenLocalVariableSpace.OFFSET); + var offset = offsetResult.value(); + context = offsetResult.context(); + + return context + .addInstruction( + new LlvmGetElementPtr( + offsetPtr, + LlvmTypes.pointer(patioDataLayoutToLlvmType(patioDataLayout)), + new LlvmOperandNull(), + patioDataOffsetsToLlvmOffsets(patioDataLayout, patioOffset) + ) + ) + .addInstruction( + new LlvmPtrToInt( + offset, + offsetPtr, + LlvmTypes.INT_64 + ) + ) + .toResult(offset); + } +} -- cgit v1.2.3