From 4be0a1caf255fa3c1dd86dfc210671b7ae395ebe Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 19 Jun 2023 19:11:44 +0100 Subject: Write directly to file when serializing LLVM IR --- .../compiler/backends/llvm/LlvmBackend.java | 18 +- .../backends/llvm/ir/LlvmIrSerializer.java | 308 +++++++++++---------- .../java/org/shedlang/compiler/util/Iterables.java | 16 +- 3 files changed, 176 insertions(+), 166 deletions(-) (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 c1ff0c95..f26de6dd 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmBackend.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmBackend.java @@ -9,6 +9,8 @@ import org.shedlang.compiler.stackir.instructions.Instruction; import org.shedlang.compiler.typechecker.TypeInfo; import org.shedlang.compiler.types.NamespaceName; +import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -63,18 +65,14 @@ public class LlvmBackend implements Backend { Path workingDirectory, String name ) throws IOException { - // TODO: write directly to file - var builder = new StringBuilder(); - LlvmIrSerializer.serializeModule(llvmModule, builder); - var llvmModulePath = workingDirectory.resolve(name + ".ll"); - var objectPath = workingDirectory.resolve(name + ".o"); Files.createDirectories(llvmModulePath.getParent()); - Files.writeString( - llvmModulePath, - builder.toString(), - StandardCharsets.UTF_8 - ); + var fileWriter = new FileWriter(llvmModulePath.toFile(), StandardCharsets.UTF_8); + try (var writer = new BufferedWriter(fileWriter)) { + LlvmIrSerializer.serializeModule(llvmModule, writer); + } + + var objectPath = workingDirectory.resolve(name + ".o"); runLlvmCompiler(llvmModulePath, objectPath); return objectPath; diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/ir/LlvmIrSerializer.java b/src/main/java/org/shedlang/compiler/backends/llvm/ir/LlvmIrSerializer.java index 47032f9f..716deb8a 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/ir/LlvmIrSerializer.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/ir/LlvmIrSerializer.java @@ -5,294 +5,296 @@ 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.*; +import java.io.IOException; + import static org.shedlang.compiler.util.Iterables.forEachInterspersed; public class LlvmIrSerializer { private LlvmIrSerializer() { } - public static void serializeBasicBlock(LlvmBasicBlock basicBlock, StringBuilder builder) { + public static void serializeBasicBlock(LlvmBasicBlock basicBlock, Appendable writer) throws IOException { if (basicBlock.label().isPresent()) { - builder.append(basicBlock.label().get()); - builder.append(":\n"); + writer.append(basicBlock.label().get()); + writer.append(":\n"); } for (var instruction : basicBlock.instructions()) { - builder.append(" "); - serializeInstruction(instruction, builder); - builder.append("\n"); + writer.append(" "); + serializeInstruction(instruction, writer); + writer.append("\n"); } - builder.append(" "); - serializeTerminatorInstruction(basicBlock.terminator(), builder); - builder.append("\n"); + writer.append(" "); + serializeTerminatorInstruction(basicBlock.terminator(), writer); + writer.append("\n"); } - public static void serializeInstruction(LlvmInstruction instruction, StringBuilder builder) { + public static void serializeInstruction(LlvmInstruction instruction, Appendable writer) throws IOException { switch (instruction) { case LlvmAdd add -> { - serializeVariable(add.target(), builder); - builder.append(" = add "); - serializeType(add.type(), builder); - builder.append(" "); - serializeOperand(add.left(), builder); - builder.append(", "); - serializeOperand(add.right(), builder); + serializeVariable(add.target(), writer); + writer.append(" = add "); + serializeType(add.type(), writer); + writer.append(" "); + serializeOperand(add.left(), writer); + writer.append(", "); + serializeOperand(add.right(), writer); } case LlvmCall call -> { if (call.target().isPresent()) { - serializeVariable(call.target().get(), builder); - builder.append(" = "); + serializeVariable(call.target().get(), writer); + writer.append(" = "); } - builder.append("call "); - serializeType(call.returnType(), builder); - builder.append(" "); - serializeOperand(call.functionPointer(), builder); - builder.append("("); + writer.append("call "); + serializeType(call.returnType(), writer); + writer.append(" "); + serializeOperand(call.functionPointer(), writer); + writer.append("("); forEachInterspersed( call.args(), - arg -> serializeTypedOperand(arg, builder), - () -> builder.append(", ") + arg -> serializeTypedOperand(arg, writer), + () -> writer.append(", ") ); - builder.append(")"); + writer.append(")"); } case LlvmGetElementPtr getElementPtr -> { - serializeVariable(getElementPtr.target(), builder); - builder.append(" = getelementptr "); - serializeType(getElementPtr.pointerType().elementType(), builder); - builder.append(", ptr "); - serializeOperand(getElementPtr.pointer(), builder); + serializeVariable(getElementPtr.target(), writer); + writer.append(" = getelementptr "); + serializeType(getElementPtr.pointerType().elementType(), writer); + writer.append(", ptr "); + serializeOperand(getElementPtr.pointer(), writer); for (var index : getElementPtr.indexes()) { - builder.append(", "); - serializeTypedOperand(index, builder); + writer.append(", "); + serializeTypedOperand(index, writer); } } case LlvmIntToPtr intToPtr -> { - serializeVariable(intToPtr.target(), builder); - builder.append(" = inttoptr "); - serializeType(intToPtr.sourceType(), builder); - builder.append(" "); - serializeOperand(intToPtr.value(), builder); - builder.append(" to ptr"); + serializeVariable(intToPtr.target(), writer); + writer.append(" = inttoptr "); + serializeType(intToPtr.sourceType(), writer); + writer.append(" "); + serializeOperand(intToPtr.value(), writer); + writer.append(" to ptr"); } case LlvmLoad load -> { - serializeVariable(load.target(), builder); - builder.append(" = load "); - serializeType(load.type(), builder); - builder.append(", ptr "); - serializeOperand(load.pointer(), builder); + serializeVariable(load.target(), writer); + writer.append(" = load "); + serializeType(load.type(), writer); + writer.append(", ptr "); + serializeOperand(load.pointer(), writer); } case LlvmPtrToInt ptrToInt -> { - serializeVariable(ptrToInt.target(), builder); - builder.append(" = ptrtoint ptr "); - serializeOperand(ptrToInt.value(), builder); - builder.append(" to "); - serializeType(ptrToInt.targetType(), builder); + serializeVariable(ptrToInt.target(), writer); + writer.append(" = ptrtoint ptr "); + serializeOperand(ptrToInt.value(), writer); + writer.append(" to "); + serializeType(ptrToInt.targetType(), writer); } case LlvmStore store -> { - builder.append("store "); - serializeType(store.type(), builder); - builder.append(" "); - serializeOperand(store.value(), builder); - builder.append(", ptr "); - serializeOperand(store.pointer(), builder); + writer.append("store "); + serializeType(store.type(), writer); + writer.append(" "); + serializeOperand(store.value(), writer); + writer.append(", ptr "); + serializeOperand(store.pointer(), writer); } } } - public static void serializeModule(LlvmModule module, StringBuilder builder) { + public static void serializeModule(LlvmModule module, Appendable writer) throws IOException { for (var topLevelEntity : module.body()) { - serializeTopLevelEntity(topLevelEntity, builder); + serializeTopLevelEntity(topLevelEntity, writer); } } - public static void serializeOperand(LlvmOperand operand, StringBuilder builder) { + public static void serializeOperand(LlvmOperand operand, Appendable writer) throws IOException { switch (operand) { case LlvmOperandArray array -> { - builder.append("["); + writer.append("["); forEachInterspersed( array.elements(), element -> { - serializeType(array.type(), builder); - builder.append(" "); - serializeOperand(element, builder); + serializeType(array.type(), writer); + writer.append(" "); + serializeOperand(element, writer); }, - () -> builder.append(", ") + () -> writer.append(", ") ); - builder.append("]"); + writer.append("]"); } case LlvmOperandInt operandInt -> { - builder.append(operandInt.value()); + writer.append(Long.toString(operandInt.value())); } case LlvmOperandNull operandNull -> { - builder.append("null"); + writer.append("null"); } case LlvmOperandStructure structure -> { - builder.append("{"); + writer.append("{"); forEachInterspersed( structure.elements(), - element -> serializeTypedOperand(element, builder), - () -> builder.append(", ") + element -> serializeTypedOperand(element, writer), + () -> writer.append(", ") ); - builder.append("}"); + writer.append("}"); } case LlvmOperandPtrToInt ptrToInt -> { - builder.append("ptrtoint (ptr "); - serializeOperand(ptrToInt.operand(), builder); - builder.append(" to "); - serializeType(ptrToInt.targetType(), builder); - builder.append(")"); + writer.append("ptrtoint (ptr "); + serializeOperand(ptrToInt.operand(), writer); + writer.append(" to "); + serializeType(ptrToInt.targetType(), writer); + writer.append(")"); } case LlvmVariable variable -> { - serializeVariable(variable, builder); + serializeVariable(variable, writer); } } } - public static void serializeParam(LlvmParam param, StringBuilder builder) { - serializeType(param.type(), builder); - builder.append(" %"); - builder.append(param.name()); + public static void serializeParam(LlvmParam param, Appendable writer) throws IOException { + serializeType(param.type(), writer); + writer.append(" %"); + writer.append(param.name()); } public static void serializeTerminatorInstruction( LlvmTerminatorInstruction instruction, - StringBuilder builder - ) { + Appendable writer + ) throws IOException { switch (instruction) { case LlvmBr br -> { - builder.append("br i1 "); - serializeOperand(br.condition(), builder); - builder.append(", label %"); - builder.append(br.ifTrueLabel()); - builder.append(", label %"); - builder.append(br.ifFalseLabel()); + writer.append("br i1 "); + serializeOperand(br.condition(), writer); + writer.append(", label %"); + writer.append(br.ifTrueLabel()); + writer.append(", label %"); + writer.append(br.ifFalseLabel()); } case LlvmBrUnconditional br -> { - builder.append("br label %"); - builder.append(br.targetLabel()); + writer.append("br label %"); + writer.append(br.targetLabel()); } case LlvmReturn returnValue -> { - builder.append("ret "); - serializeType(returnValue.type(), builder); - builder.append(" "); - serializeOperand(returnValue.value(), builder); + writer.append("ret "); + serializeType(returnValue.type(), writer); + writer.append(" "); + serializeOperand(returnValue.value(), writer); } case LlvmReturnVoid returnVoid -> { - builder.append("ret void"); + writer.append("ret void"); } } } - public static void serializeTopLevelEntity(LlvmTopLevelEntity entity, StringBuilder builder) { + public static void serializeTopLevelEntity(LlvmTopLevelEntity entity, Appendable writer) throws IOException { switch (entity) { case LlvmFunctionDeclaration functionDeclaration -> { - builder.append("declare "); - serializeType(functionDeclaration.returnType(), builder); - builder.append(" @"); - builder.append(functionDeclaration.name()); - builder.append("("); + writer.append("declare "); + serializeType(functionDeclaration.returnType(), writer); + writer.append(" @"); + writer.append(functionDeclaration.name()); + writer.append("("); forEachInterspersed( functionDeclaration.params(), - param -> serializeParam(param, builder), - () -> builder.append(", ") + param -> serializeParam(param, writer), + () -> writer.append(", ") ); - builder.append(")\n"); + writer.append(")\n"); } case LlvmFunctionDefinition functionDefinition -> { - builder.append("define "); - serializeType(functionDefinition.returnType(), builder); - builder.append(" @"); - builder.append(functionDefinition.name()); - builder.append("("); + writer.append("define "); + serializeType(functionDefinition.returnType(), writer); + writer.append(" @"); + writer.append(functionDefinition.name()); + writer.append("("); forEachInterspersed( functionDefinition.params(), - param -> serializeParam(param, builder), - () -> builder.append(", ") + param -> serializeParam(param, writer), + () -> writer.append(", ") ); - builder.append(") {\n"); + writer.append(") {\n"); for (var basicBlock : functionDefinition.body()) { - serializeBasicBlock(basicBlock, builder); + serializeBasicBlock(basicBlock, writer); } - builder.append("}\n"); + writer.append("}\n"); } case LlvmGlobalDefinition globalDefinition -> { - builder.append("@"); - builder.append(globalDefinition.name()); - builder.append(" = "); + writer.append("@"); + writer.append(globalDefinition.name()); + writer.append(" = "); if (globalDefinition.isPrivate()) { - builder.append("private "); + writer.append("private "); } if (globalDefinition.unnamedAddr()) { - builder.append("unnamed_addr "); + writer.append("unnamed_addr "); } if (globalDefinition.value().isEmpty()) { - builder.append("external "); + writer.append("external "); } - builder.append(globalDefinition.constant() ? "constant" : "global"); - builder.append(" "); - serializeType(globalDefinition.type(), builder); + writer.append(globalDefinition.constant() ? "constant" : "global"); + writer.append(" "); + serializeType(globalDefinition.type(), writer); if (globalDefinition.value().isPresent()) { - builder.append(" "); - serializeOperand(globalDefinition.value().get(), builder); + writer.append(" "); + serializeOperand(globalDefinition.value().get(), writer); } - builder.append("\n"); + writer.append("\n"); } } } - public static void serializeType(LlvmType type, StringBuilder builder) { + public static void serializeType(LlvmType type, Appendable writer) throws IOException { switch (type) { case LlvmTypeScalar scalar -> { - builder.append(scalar.name()); + writer.append(scalar.name()); } case LlvmTypeArray array -> { - builder.append("["); - builder.append(array.size().orElse(0)); - builder.append(" x "); - serializeType(array.elementType(), builder); - builder.append("]"); + writer.append("["); + writer.append(Integer.toString(array.size().orElse(0))); + writer.append(" x "); + serializeType(array.elementType(), writer); + writer.append("]"); } case LlvmTypeStructure structure -> { - builder.append("{"); + writer.append("{"); forEachInterspersed( structure.elementTypes(), - elementType -> serializeType(elementType, builder), - () -> builder.append(", ") + elementType -> serializeType(elementType, writer), + () -> writer.append(", ") ); - builder.append("}"); + writer.append("}"); } case LlvmTypePointer pointer -> { - builder.append("ptr"); + writer.append("ptr"); } case LlvmTypeFunction function -> { - serializeType(function.returnType(), builder); - builder.append(" ("); + serializeType(function.returnType(), writer); + writer.append(" ("); forEachInterspersed( function.paramTypes(), - paramType -> serializeType(paramType, builder), - () -> builder.append(", ") + paramType -> serializeType(paramType, writer), + () -> writer.append(", ") ); - builder.append(")"); + writer.append(")"); } } } - public static void serializeTypedOperand(LlvmTypedOperand typedOperand, StringBuilder builder) { - serializeType(typedOperand.type(), builder); - builder.append(" "); - serializeOperand(typedOperand.operand(), builder); + public static void serializeTypedOperand(LlvmTypedOperand typedOperand, Appendable writer) throws IOException { + serializeType(typedOperand.type(), writer); + writer.append(" "); + serializeOperand(typedOperand.operand(), writer); } - public static void serializeVariable(LlvmVariable variable, StringBuilder builder) { + public static void serializeVariable(LlvmVariable variable, Appendable writer) throws IOException { switch (variable) { case LlvmVariableGlobal global -> { - builder.append("@"); - builder.append(global.name()); + writer.append("@"); + writer.append(global.name()); } case LlvmVariableLocal local -> { - builder.append("%"); - builder.append(local.name()); + writer.append("%"); + writer.append(local.name()); } } } diff --git a/src/main/java/org/shedlang/compiler/util/Iterables.java b/src/main/java/org/shedlang/compiler/util/Iterables.java index 6fcb02e1..ff043c13 100644 --- a/src/main/java/org/shedlang/compiler/util/Iterables.java +++ b/src/main/java/org/shedlang/compiler/util/Iterables.java @@ -1,12 +1,22 @@ package org.shedlang.compiler.util; -import java.util.function.Consumer; - public class Iterables { private Iterables() { } - public static void forEachInterspersed(Iterable values, Consumer onElement, Action between) { + public interface OnElement { + void accept(T value) throws E; + } + + public interface Between { + void call() throws E; + } + + public static void forEachInterspersed( + Iterable values, + OnElement onElement, + Between between + ) throws E { var first = true; for (var value : values) { if (!first) { -- cgit v1.2.3