Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend bootstrap compiler #489

Merged
merged 1 commit into from Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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