Skip to content

Commit

Permalink
Extend bootstrap compiler (#489)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Mar 3, 2024
1 parent 9c6888b commit d7c9175
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .run/spice.run.xml
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O2 -d ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_BUILD_INCLUDE_DIR" value="D:/LLVM/build-release/include" />
<env name="LLVM_INCLUDE_DIR" value="D:/LLVM/llvm/include" />
Expand Down
71 changes: 13 additions & 58 deletions media/test-project/test.spice
@@ -1,62 +1,17 @@
import "bootstrap/bindings/llvm/llvm" as llvm;
import "std/data/vector";
import "std/os/env";
import "bootstrap/lexer/lexer";

f<int> main() {
llvm::initializeNativeTarget();
llvm::initializeNativeAsmPrinter();

heap string targetTriple = llvm::getDefaultTargetTriple();
string error;
llvm::Target target = llvm::getTargetFromTriple(targetTriple, &error);
llvm::TargetMachine targetMachine = target.createTargetMachine(targetTriple, "generic", "", llvm::LLVMCodeGenOptLevel::Default, llvm::LLVMRelocMode::Default, llvm::LLVMCodeModel::Default);

llvm::LLVMContext context;
llvm::Module module = llvm::Module("test", context);
module.setDataLayout(targetMachine.createDataLayout());
//module.setTargetTriple(targetTriple); // This emits target dependent information in the IR, which is not what we want here.
llvm::Builder builder = llvm::Builder(context);

llvm::Type returnType = builder.getInt32Ty();
Vector<llvm::Type> argTypes;
llvm::Type funcType = llvm::getFunctionType(returnType, argTypes);
llvm::Function func = llvm::Function(module, "main", funcType);
func.setLinkage(llvm::LLVMLinkage::ExternalLinkage);

llvm::BasicBlock entry = llvm::BasicBlock(context, "");
func.pushBack(entry);
builder.setInsertPoint(entry);

llvm::Value calcResult = builder.createAdd(builder.getInt32(1), builder.getInt32(2), "calcResult");

llvm::Value helloWorldStr = builder.createGlobalStringPtr("Hello, world!\n", "helloWorldStr");
Vector<llvm::Type> printfArgTypes;
printfArgTypes.pushBack(builder.getPtrTy());
printfArgTypes.pushBack(builder.getInt32Ty());
llvm::Type printfFuncType = llvm::getFunctionType(builder.getInt32Ty(), printfArgTypes, true);
llvm::Function printfFunc = module.getOrInsertFunction("printf", printfFuncType);

Vector<llvm::Value> printfArgs;
printfArgs.pushBack(helloWorldStr);
printfArgs.pushBack(calcResult);
builder.createCall(printfFunc, printfArgs);

builder.createRet(builder.getInt32(0));

assert !llvm::verifyFunction(func);
string output;
assert !llvm::verifyModule(module, &output);

printf("Unoptimized IR:\n%s", module.print());

llvm::PassBuilderOptions pto;
llvm::PassBuilder passBuilder = llvm::PassBuilder(pto);
passBuilder.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2);
passBuilder.addPass(llvm::AlwaysInlinerPass());
passBuilder.run(module, targetMachine);

printf("Optimized IR:\n%s", module.print());

targetMachine.emitToFile(module, "this-is-a-test.o", llvm::LLVMCodeGenFileType::ObjectFile);
String filePath = getEnv("SPICE_STD_DIR") + "/../test/test-files/bootstrap-compiler/standalone-lexer-test/test-file.spice";
Lexer lexer = Lexer(filePath.getRaw());
unsigned long tokenCount = 0l;
while (!lexer.isEOF()) {
Token token = lexer.getToken();
token.print();
lexer.advance();
tokenCount++;
}
printf("\nLexed tokens: %d\n", tokenCount);
}

/*import "bootstrap/util/block-allocator";
Expand All @@ -65,5 +20,5 @@ import "bootstrap/util/memory";
f<int> main() {
DefaultMemoryManager defaultMemoryManager;
IMemoryManager* memoryManager = &defaultMemoryManager;
BlockAllocator<int> allocator = BlockAllocator<int>(memoryManager, 10);
BlockAllocator<int> allocator = BlockAllocator<int>(memoryManager, 10l);
}*/
16 changes: 9 additions & 7 deletions src-bootstrap/exception/cli-error.spice
Expand Up @@ -32,11 +32,13 @@ public p CliError.ctor(const CliErrorType errorType, const string message) {
* @return Prefix string for the error type
*/
f<string> CliError.getMessagePrefix(const CliErrorType errorType) {
if errorType == CliErrorType.INCOMPLETE_TARGET_TRIPLE { return "Incomplete target triple"; }
if errorType == CliErrorType.INVALID_TARGET_TRIPLE { return "Invalid target triple"; }
if errorType == CliErrorType.SOURCE_FILE_MISSING { return "Source file missing"; }
if errorType == CliErrorType.OPT_DEBUG_INFO_INCOMPATIBILITY { return "Cannot emit debug info with optimization enabled"; }
if errorType == CliErrorType.NON_ZERO_EXIT_CODE { return "Non-zero exit code"; }
if errorType == CliErrorType.COMING_SOON_CLI { return "Coming soon"; }
return "Unknown error";
switch errorType {
case CliErrorType::INCOMPLETE_TARGET_TRIPLE: { return "Incomplete target triple"; }
case CliErrorType::INVALID_TARGET_TRIPLE: { return "Invalid target triple"; }
case CliErrorType::SOURCE_FILE_MISSING: { return "Source file missing"; }
case CliErrorType::OPT_DEBUG_INFO_INCOMPATIBILITY: { return "Cannot emit debug info with optimization enabled"; }
case CliErrorType::NON_ZERO_EXIT_CODE: { return "Non-zero exit code"; }
case CliErrorType::COMING_SOON_CLI: { return "Coming soon"; }
default: { return "Unknown error"; }
}
}
8 changes: 5 additions & 3 deletions src-bootstrap/exception/lexer-error.spice
@@ -1,4 +1,4 @@
// Imports
// Own imports
import "../util/code-loc";

public type LexerErrorType enum {
Expand Down Expand Up @@ -30,6 +30,8 @@ public p ParserError.ctor(const CodeLoc* codeLoc, const LexerErrorType errorType
* @return Prefix string for the error type
*/
f<string> ParserError.getMessagePrefix(const LexerErrorType errorType) {
if errorType == LexerErrorType.TOKENIZING_FAILED { return "Parsing failed"; }
return "Unknown error";
switch errorType {
case LexerErrorType::TOKENIZING_FAILED: { return "Tokenizing failed"; }
default: { return "Unknown error"; }
}
}
10 changes: 6 additions & 4 deletions src-bootstrap/exception/linker-error.spice
@@ -1,4 +1,4 @@
// Imports
// Own imports
import "../util/code-loc";

public type LinkerErrorType enum {
Expand Down Expand Up @@ -28,7 +28,9 @@ public p LinkerError.ctor(const LinkerErrorType errorType, const string message)
* @return Prefix string for the error type
*/
f<string> LinkerError.getMessagePrefix(const LinkerErrorType errorType) {
if errorType == LinkerErrorType.PARSING_FAILED { return "Parsing failed"; }
if errorType == LinkerErrorType.NUMBER_OUT_OF_RANGE { return "Number is out of range"; }
return "Unknown error";
switch errorType {
case LinkerErrorType::LINKER_NOT_FOUND: { return "Linker not found"; }
case LinkerErrorType::LINKER_ERROR: { return "Linker error occurred"; }
default: { return "Unknown error"; }
}
}
18 changes: 13 additions & 5 deletions src-bootstrap/exception/parser-error.spice
Expand Up @@ -4,7 +4,10 @@ import "../util/code-loc";
public type ParserErrorType enum {
PARSING_FAILED,
NUMBER_OUT_OF_RANGE,
INVALID_CHAR_LITERAL
INVALID_SPECIFIER_COMBINATION,
INVALID_CHAR_LITERAL,
INVALID_ATTR_VALUE_TYPE,
RESERVED_KEYWORD
}

/**
Expand Down Expand Up @@ -32,8 +35,13 @@ public p ParserError.ctor(const CodeLoc* codeLoc, const ParserErrorType errorTyp
* @return Prefix string for the error type
*/
f<string> ParserError.getMessagePrefix(const ParserErrorType errorType) {
if errorType == ParserErrorType.PARSING_FAILED { return "Parsing failed"; }
if errorType == ParserErrorType.NUMBER_OUT_OF_RANGE { return "Number is out of range"; }
if errorType == ParserErrorType.INVALID_CHAR_LITERAL { return "Invalid char literal"; }
return "Unknown error";
switch errorType {
case ParserErrorType::PARSING_FAILED: { return "Parsing failed"; }
case ParserErrorType::NUMBER_OUT_OF_RANGE: { return "Number is out of range"; }
case ParserErrorType::INVALID_SPECIFIER_COMBINATION: { return "Invalid specifier combination"; }
case ParserErrorType::INVALID_CHAR_LITERAL: { return "Invalid char literal"; }
case ParserErrorType::INVALID_ATTR_VALUE_TYPE: { return "Invalid attribute value type"; }
case ParserErrorType::RESERVED_KEYWORD: { return "Usage of reserved keyword"; }
default: { return "Unknown error"; }
}
}
58 changes: 45 additions & 13 deletions src-bootstrap/exception/semantic-error.spice
Expand Up @@ -6,67 +6,98 @@ public type SemanticErrorType enum {
REFERENCED_UNDEFINED_FUNCTION,
REFERENCED_UNDEFINED_VARIABLE,
REFERENCED_UNDEFINED_STRUCT,
REFERENCED_UNDEFINED_FIELD,
USED_BEFORE_DECLARED,
FUNCTION_AMBIGUITY,
STRUCT_AMBIGUITY,
INTERFACE_AMBIGUITY,
DUPLICATE_SYMBOL,
VARIABLE_DECLARED_TWICE,
GLOBAL_DECLARED_TWICE,
FUNCTION_DECLARED_TWICE,
GENERIC_TYPE_DECLARED_TWICE,
STRUCT_WITH_ILLEGAL_NAME,
STRUCT_INFINITE_SIZE,
STRUCT_DECLARED_TWICE,
MISSING_NO_ARGS_CTOR,
INTERFACE_DECLARED_TWICE,
INTERFACE_METHOD_NOT_IMPLEMENTED,
ENUM_DECLARED_TWICE,
INVALID_SYMBOL_ACCESS,
DUPLICATE_ENUM_ITEM_NAME,
DUPLICATE_ENUM_ITEM_VALUE,
GLOBAL_OF_TYPE_DYN,
GLOBAL_OF_INVALID_TYPE,
GLOBAL_CONST_WITHOUT_VALUE,
MISSING_RETURN_STMT,
INVALID_PARAM_ORDER,
LAMBDA_WITH_OPTIONAL_PARAMS,
REFERENCED_OVERLOADED_FCT,
DTOR_MUST_BE_PROCEDURE,
DTOR_WITH_PARAMS,
OPERATOR_WRONG_DATA_TYPE,
INVALID_ITERATOR,
UNEXPECTED_DYN_TYPE,
REASSIGN_CONST_VARIABLE,
CONDITION_MUST_BE_BOOL,
SWITCH_EXPR_MUST_BE_PRIMITIVE,
SWITCH_CASE_TYPE_MISMATCH,
MISSING_MAIN_FUNCTION,
FCT_PARAM_IS_TYPE_DYN,
INVALID_BREAK_NUMBER,
INVALID_CONTINUE_NUMBER,
FALLTHROUGH_NOT_ALLOWED,
CASE_CONSTANT_NOT_ENUM,
PRINTF_TYPE_ERROR,
PRINTF_ARG_COUNT_ERROR,
STD_NOT_FOUND,
DUPLICATE_IMPORT_NAME,
IMPORTED_FILE_NOT_EXISTING,
CIRCULAR_DEPENDENCY,
ACCESS_TO_NON_EXISTING_MEMBER,
INVALID_MEMBER_ACCESS,
SCOPE_ACCESS_ONLY_IMPORTS,
UNKNOWN_DATATYPE,
UNKNOWN_ATTR,
INVALID_ATTR_TARGET,
MISSING_ATTR_VALUE,
NUMBER_OF_FIELDS_NOT_MATCHING,
FIELD_TYPE_NOT_MATCHING,
ARRAY_SIZE_INVALID,
FOREACH_IDX_NOT_LONG,
ARRAY_INDEX_NOT_INT_OR_LONG,
ARRAY_ITEM_TYPE_NOT_MATCHING,
EXPECTED_ARRAY_TYPE,
SIZEOF_DYNAMIC_SIZED_ARRAY,
EXPECTED_ERROR_TYPE,
RETURN_WITHOUT_VALUE_RESULT,
RETURN_WITH_VALUE_IN_PROCEDURE,
INVALID_STRUCT_INSTANTIATION,
DYN_POINTERS_NOT_ALLOWED,
REF_POINTERS_ARE_NOT_ALLOWED,
DYN_REFERENCES_NOT_ALLOWED,
MULTI_REF_NOT_ALLOWED,
DYN_ARRAYS_NOT_ALLOWED,
REFERENCE_WITHOUT_INITIALIZER,
TEMP_TO_NON_CONST_REF,
GENERIC_TYPE_NOT_IN_TEMPLATE,
SPECIFIER_AT_ILLEGAL_CONTEXT,
INSUFFICIENT_VISIBILITY,
TID_INVALID,
JOIN_ARG_MUST_BE_TID,
EXPECTED_GENERIC_TYPE,
EXPECTED_NON_GENERIC_TYPE,
EXPECTED_STRUCT_TYPE,
EXPECTED_INTERFACE_TYPE,
ALIAS_WITH_TEMPLATE_LIST,
INTERFACE_WITH_TEMPLATE_LIST,
INVALID_TEMPLATE_TYPES,
EXPECTED_VALUE,
EXPECTED_TYPE,
UNSAFE_OPERATION_IN_SAFE_CONTEXT,
ASSERTION_CONDITION_BOOL,
ARRAY_INDEX_OUT_OF_BOUNDS,
RESERVED_KEYWORD,
EXPECTED_CONST_VARIABLE,
DIVISION_BY_ZERO,
TEST_FUNCTION_WITH_PARAMS,
TEST_FUNCTION_WRONG_RETURN_TYPE,
COMING_SOON_SA
}

Expand All @@ -86,13 +117,14 @@ public p SemanticError.ctor(const AstNode* node, const SemanticErrorType errorTy
* @return Prefix string for the error type
*/
f<string> SemanticError.getMessagePrefix(const SemanticErrorType errorType) {
if errorType == SemanticErrorType.REFERENCED_UNDEFINED_FUNCTION { return "Referenced undefined function"; }
if errorType == SemanticErrorType.REFERENCED_UNDEFINED_VARIABLE { return "Referenced undefined variable"; }
if errorType == SemanticErrorType.REFERENCED_UNDEFINED_STRUCT { return "Referenced undefined struct"; }
if errorType == SemanticErrorType.FUNCTION_AMBIGUITY { return "Function ambiguity"; }
if errorType == SemanticErrorType.STRUCT_AMBIGUITY { return "Struct ambiguity"; }
if errorType == SemanticErrorType.VARIABLE_DECLARED_TWICE { return "Multiple declarations of the same variable"; }
if errorType == SemanticErrorType.FUNCTION_DECLARED_TWICE { return "Multiple declarations of a function/procedure"; }
// ToDo: Extend
return "Unknown error";
switch errorType {
case SemanticErrorType::REFERENCED_UNDEFINED_FUNCTION: { return "Referenced undefined function"; }
case SemanticErrorType::REFERENCED_UNDEFINED_VARIABLE: { return "Referenced undefined variable"; }
case SemanticErrorType::REFERENCED_UNDEFINED_STRUCT: { return "Referenced undefined struct"; }
case SemanticErrorType::FUNCTION_AMBIGUITY: { return "Function ambiguity"; }
case SemanticErrorType::STRUCT_AMBIGUITY: { return "Struct ambiguity"; }
case SemanticErrorType::VARIABLE_DECLARED_TWICE: { return "Multiple declarations of the same variable"; }
case SemanticErrorType::FUNCTION_DECLARED_TWICE: { return "Multiple declarations of a function/procedure"; }
default: { return "Unknown error"; }
}
}
70 changes: 69 additions & 1 deletion src-bootstrap/irgenerator/ir-generator.spice
@@ -1,7 +1,75 @@
// Std imports
import "std/data/vector";
import "std/data/stack";

// Own imports
import "bindings/llvm/llvm" as llvm;

public type Generator struct {
type CommonLLVMTypes struct {
llvm::StructType fatPtrType
}

public type IRGenerator struct/* : ICompilerPass*/ {
llvm::Context& context
llvm::Builder& builder
llvm::Module* module
//OpRuleConversionManager conversionManager
//StdFunctionManager stdFunctionManager
//DebugInfoGenerator diGenerator
CommonLLVMTypes llvmTypes
Vector<llvm::BasicBlock> breakBlocks
Vector<llvm::BasicBlock> continueBlocks
Stack<llvm::BasicBlock> fallthroughBlocks
llvm::BasicBlock* allocaInsertBlock = nil<llvm::BasicBlock*>
llvm::Instruction* allocaInsertInst = nil<llvm::Instruction*>
bool blockAlreadyTerminated = false
//Vector<DeferredLogic> deferredVTableInitializations
}

public p IRGenerator.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile) {
this.context = resourceManager.context;
this.builder = resourceManager.builder;
this.module = sourceFile.module;
this.stdFunctionManager = StdFunctionManager(resourceManager, sourceFile.llvmModule);

// Attach information to the module
this.module.setTargetTriple(this.cliOptions.targetTriple);
this.module.setDataLayout(resourceManager.targetMachine.createDataLayout());

// Initialiize debug info generator
if this.cliOptions.generateDebugInfo {
this.diGenerator.initialize(sourceFile.fileName, sourceFile.fileDir);
}
}

public f<llvm::Value> IRGenerator.createAlloca(llvm::Type llvmType, String varName) {
if !this.cliOptions.namesForIRValues { varName.clear(); }

if this.allocaInsertInst != nil<llvm::Instruction*> { // If there is already an alloca inst, insert right after that
llvm::AllocaInst allocaInst = this.builder.createAlloca(llvmType, llvm::Value(), varName);
allocaInst.setDebugLoc(llvm::DebugLoc());
allocaInstrInst = allocaInst;
} else { // This is the first alloca inst in the current function -> insert at the entry block
// Save current basic block and move insert cursor to entry block of the current function
llvm::BasicBlock currentBlock = this.builder.getInsertBlock();
this.builder.setInsertPoint(this.allocaInsertBlock);

// Allocate the size of the given LLVM type
this.allocaInsertInst = this.builder.createAlloca(llvmType, llvm::Value(), varName);
this.allocaInsertInst.setDebugLoc(llvm::DebugLoc());

// Resotre old basic block
this.builder.setInsertPoint(currentBlock);
}
return (llvm::Value) allocaInsertInst;
}

public f<llvm::Value> IRGenerator.insertLoad(llvm::Type llvmType, llvm::Value ptr, bool isVolatile, const String& varName = "") {
assert ptr.getType().isPointerTy();
return this.builder.createLoad(llvmType, ptr, isVolatile, this.cliOptions.namesForIRValues ? varName.getRaw() : "");
}

public p IRGenerator.insertStore(llvm::Value value, llvm::Value ptr, bool isVolatile) {
assert ptr.getType().isPointerTy();
this.builder.createStore(value, ptr, isVolatile);
}
1 change: 1 addition & 0 deletions src-bootstrap/lexer/lexer.spice
Expand Up @@ -12,6 +12,7 @@ import "../reader/reader";
import "../reader/code-loc";

public type Lexer struct/* : ICompilerPass*/ {
//compose CompilerPass compilerPass
Reader reader
Token curTok
}
Expand Down

0 comments on commit d7c9175

Please sign in to comment.