diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-06-16 22:50:45 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-06-16 22:50:45 +0100 |
| commit | dbb847a2e9524e1a6479c13ea2379dfa453bb32b (patch) | |
| tree | 1d589b67275a9a4948dc6c1bfb0e455b93972056 /src | |
| parent | fa21920d943f199609712461cb700418d6d4ead0 (diff) | |
Generate top level entity for minimal function
Diffstat (limited to 'src')
6 files changed, 162 insertions, 5 deletions
diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompiler.java b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompiler.java index 7fc14092..2ad82eef 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompiler.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompiler.java @@ -1,5 +1,9 @@ package org.shedlang.compiler.backends.llvm; +import org.shedlang.compiler.backends.llvm.ir.ast.LlvmBasicBlock; +import org.shedlang.compiler.backends.llvm.ir.ast.LlvmFunctionDefinition; +import org.shedlang.compiler.backends.llvm.ir.ast.LlvmParam; +import org.shedlang.compiler.backends.llvm.ir.ast.instructions.LlvmReturn; import org.shedlang.compiler.backends.llvm.ir.ast.operands.LlvmOperand; import org.shedlang.compiler.backends.llvm.ir.ast.operands.LlvmOperandInt; import org.shedlang.compiler.stackir.instructions.*; @@ -49,8 +53,38 @@ public class LlvmCompiler { args ); } - case DefineFunctionInstruction defineFunctionInstruction -> { - throw new UnsupportedOperationException("TODO"); + case DefineFunctionInstruction defineFunction -> { + var topLevelEntityName = context.generateTopLevelEntityName( + LlvmCompilerTopLevelEntitySpace.FUNCTION, + defineFunction.name() + ); + + var functionContext = context.enterFunction(); + for (var bodyInstruction : defineFunction.body()) { + functionContext = compileInstruction(bodyInstruction, functionContext); + } + // TODO: rejoin function context with context + // TODO: assert stack has exactly one element + var returnValue = functionContext.stack().get(0); + + yield context.addTopLevelEntity(new LlvmFunctionDefinition( + topLevelEntityName, + LlvmCompilerTypes.VALUE_TYPE, + List.of( + new LlvmParam( + LlvmCompilerClosures.typeClosureEnvironmentPointer(), + // TODO: allocate name properly + "shed__env__0" + ) + ), + List.of( + new LlvmBasicBlock( + Optional.empty(), + List.of(), + new LlvmReturn(LlvmCompilerTypes.VALUE_TYPE, returnValue) + ) + ) + )); } case DiscardInstruction discardInstruction -> context.pop().context(); diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerContext.java b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerContext.java index e1925e24..d558f483 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerContext.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerContext.java @@ -93,21 +93,36 @@ public record LlvmCompilerContext( builder.unnamedAddr() ); + return addTopLevelEntity(globalDefinition).toResult(globalDefinition); + } + + public LlvmCompilerContext addTopLevelEntity(LlvmTopLevelEntity topLevelEntity) { return new LlvmCompilerContext( typeInfo, stack, locals, instructions, - topLevelEntitiesByName.plus(globalDefinition.name(), globalDefinition), + topLevelEntitiesByName.plus(topLevelEntity.name(), topLevelEntity), localVariableNames - ).toResult(globalDefinition); + ); + } + + public String generateTopLevelEntityName(LlvmCompilerTopLevelEntitySpace space, String name) { + return generateTopLevelEntityName(space, Optional.of(name)); } private String generateTopLevelEntityName(LlvmCompilerTopLevelEntitySpace space) { + return generateTopLevelEntityName(space, Optional.empty()); + } + + private String generateTopLevelEntityName( + LlvmCompilerTopLevelEntitySpace space, + Optional<String> name + ) { return NameAllocator.generateName( topLevelEntitiesByName::containsKey, "shed__" + space, - Optional.empty() + name ); } @@ -146,4 +161,15 @@ public record LlvmCompilerContext( public <T> LlvmCompilerResult<T> toResult(T value) { return new LlvmCompilerResult<>(value, this); } + + public LlvmCompilerContext enterFunction() { + return new LlvmCompilerContext( + typeInfo, + Empty.stack(), + Empty.map(), + Empty.vector(), + topLevelEntitiesByName, + NameAllocator.empty() + ); + } } diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerTopLevelEntitySpace.java b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerTopLevelEntitySpace.java index 7ca7a06b..87b0a60b 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerTopLevelEntitySpace.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCompilerTopLevelEntitySpace.java @@ -1,6 +1,7 @@ package org.shedlang.compiler.backends.llvm; public enum LlvmCompilerTopLevelEntitySpace { + FUNCTION("fun"), STRING("string"); private final String name; diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmTopLevelEntity.java b/src/main/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmTopLevelEntity.java index 86b3efc3..b1cabfa0 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmTopLevelEntity.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmTopLevelEntity.java @@ -1,4 +1,5 @@ package org.shedlang.compiler.backends.llvm.ir.ast; public sealed interface LlvmTopLevelEntity permits LlvmFunctionDefinition, LlvmGlobalDefinition { + String name(); } diff --git a/src/test/java/org/shedlang/compiler/backends/llvm/LlvmCompilerDefineFunctionTests.java b/src/test/java/org/shedlang/compiler/backends/llvm/LlvmCompilerDefineFunctionTests.java new file mode 100644 index 00000000..1575c2ad --- /dev/null +++ b/src/test/java/org/shedlang/compiler/backends/llvm/LlvmCompilerDefineFunctionTests.java @@ -0,0 +1,63 @@ +package org.shedlang.compiler.backends.llvm; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.backends.llvm.ir.ast.LlvmFunctionDefinition; +import org.shedlang.compiler.backends.llvm.ir.ast.operands.LlvmOperand; +import org.shedlang.compiler.sources.Sources; +import org.shedlang.compiler.stackir.instructions.DefineFunctionInstruction; +import org.shedlang.compiler.stackir.instructions.Instructions; +import org.zwobble.precisely.Matcher; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.shedlang.compiler.backends.llvm.ir.ast.LlvmIrMatchers.*; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.*; + +public class LlvmCompilerDefineFunctionTests { + @Test + public void canDefineMinimalFunction() { + var instruction = new DefineFunctionInstruction( + "f", + List.of(), + List.of(), + Set.of(), + List.of( + Instructions.pushUnit() + ), + Sources.NULL + ); + var context = LlvmCompilerContext.EMPTY; + + var result = LlvmCompiler.compileInstruction(instruction, context); + + assertThat(result.topLevelEntities(), isSequence( + instanceOf( + LlvmFunctionDefinition.class, + has("name", x -> x.name(), equalTo("shed__fun__f__0")), + has("returnType", x -> x.returnType(), equalTo(LlvmCompilerTypes.VALUE_TYPE)), + has("params", x -> x.params(), isSequence( + isLlvmParam(LlvmCompilerClosures.typeClosureEnvironmentPointer(), "shed__env__0") + )), + has("body", x -> x.body(), isSequence( + isLlvmBasicBlock( + isSequence(), + isLlvmReturn(LlvmCompilerTypes.VALUE_TYPE, isLlvmOperandInt(0)) + ) + )) + ) + )); + assertThat(result.instructions(), isSequence()); + assertThat(result.stack(), isSequence()); + assertThat(result.locals().entrySet(), isSequence()); + } + + private static Matcher<Map.Entry<String, LlvmOperand>> isLocal(String name, Matcher<LlvmOperand> operand) { + return allOf( + has("name", x -> x.getKey(), equalTo(name)), + has("operand", x -> x.getValue(), operand) + ); + } +} diff --git a/src/test/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmIrMatchers.java b/src/test/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmIrMatchers.java index 21755f6e..f80746e1 100644 --- a/src/test/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmIrMatchers.java +++ b/src/test/java/org/shedlang/compiler/backends/llvm/ir/ast/LlvmIrMatchers.java @@ -1,5 +1,8 @@ package org.shedlang.compiler.backends.llvm.ir.ast; +import org.shedlang.compiler.backends.llvm.ir.ast.instructions.LlvmInstruction; +import org.shedlang.compiler.backends.llvm.ir.ast.instructions.LlvmReturn; +import org.shedlang.compiler.backends.llvm.ir.ast.instructions.LlvmTerminatorInstruction; import org.shedlang.compiler.backends.llvm.ir.ast.operands.*; import org.shedlang.compiler.backends.llvm.ir.ast.types.LlvmType; import org.zwobble.precisely.Matcher; @@ -10,6 +13,17 @@ public class LlvmIrMatchers { private LlvmIrMatchers() { } + public static Matcher<LlvmBasicBlock> isLlvmBasicBlock( + Matcher<Iterable<? extends LlvmInstruction>> instructions, + Matcher<LlvmTerminatorInstruction> terminator + ) { + return allOf( + has("label", x -> x.label(), isOptionalEmpty()), + has("instructions", x -> x.instructions(), instructions), + has("terminator", x -> x.terminator(), terminator) + ); + } + public static Matcher<LlvmOperand> isLlvmOperandInt(long value) { return instanceOf( LlvmOperandInt.class, @@ -30,6 +44,24 @@ public class LlvmIrMatchers { ); } + public static Matcher<LlvmParam> isLlvmParam(LlvmType type, String name) { + return allOf( + has("type", x -> x.type(), equalTo(type)), + has("name", x -> x.name(), equalTo(name)) + ); + } + + public static Matcher<LlvmTerminatorInstruction> isLlvmReturn( + LlvmType type, + Matcher<LlvmOperand> value + ) { + return instanceOf( + LlvmReturn.class, + has("type", x -> x.type(), equalTo(type)), + has("value", x -> x.value(), value) + ); + } + public static Matcher<LlvmTypedOperand> isLlvmTypedOperand(LlvmType type, Matcher<LlvmOperand> operand) { return allOf( has("type", x -> x.type(), equalTo(type)), |
