diff --git a/CMakeLists.txt b/CMakeLists.txt index 91d8d5b5e..19b541d05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options(/permissive-) add_compile_options(/Zc:inline) add_compile_options(/Gy) # Function-level linking + add_compile_options(/D_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING) # Ignore warnings in external headers add_compile_options(/experimental:external /external:anglebrackets /external:W0) diff --git a/include/slang/codegen/CodeGenerator.h b/include/slang/codegen/CodeGenerator.h index 0adf09379..cfed36edc 100644 --- a/include/slang/codegen/CodeGenerator.h +++ b/include/slang/codegen/CodeGenerator.h @@ -7,34 +7,77 @@ //------------------------------------------------------------------------------ #pragma once +#include #include #include +#include #include "slang/util/Util.h" namespace llvm { +class BasicBlock; +class Constant; +class Function; class LLVMContext; class Module; +class Type; +class Value; } // namespace llvm namespace slang { class Compilation; +class ConstantValue; +class Expression; +class InstanceSymbol; +class ProceduralBlockSymbol; +class Statement; +class SubroutineSymbol; +class SVInt; class Symbol; +class SystemSubroutine; +class Type; +class VariableSymbol; + +struct CodegenOptions { + uint32_t maxIntBits = 128; + bool flattenFourState = false; +}; class CodeGenerator { public: CodeGenerator(Compilation& compilation); ~CodeGenerator(); - std::string run(const Symbol& symbol); + std::string finish(); + + void genInstance(const InstanceSymbol& instance); + void genStmt(llvm::BasicBlock* bb, const Statement& stmt); + + llvm::Constant* genConstant(const Type& type, const ConstantValue& cv); + llvm::Constant* genConstant(const Type& type, const SVInt& integer); + llvm::Type* genType(const Type& type); + llvm::Value* genExpr(llvm::BasicBlock* bb, const Expression& expr); + llvm::Function* genSubroutine(const SubroutineSymbol& subroutine); + llvm::Function* genSubroutine(const SystemSubroutine& subroutine); private: + void genGlobal(const VariableSymbol& variable); + void genBlock(const ProceduralBlockSymbol& variable); + std::unique_ptr ctx; std::unique_ptr module; + flat_hash_map typeMap; + flat_hash_map globalMap; + flat_hash_map userSubroutineMap; + flat_hash_map sysSubroutineMap; + llvm::BasicBlock* globalInitBlock; + std::vector initialBlocks; + llvm::Function* mainFunc; Compilation& compilation; + CodegenOptions options; }; } // namespace slang \ No newline at end of file diff --git a/include/slang/codegen/ExpressionEmitter.h b/include/slang/codegen/ExpressionEmitter.h new file mode 100644 index 000000000..d4c8e42bb --- /dev/null +++ b/include/slang/codegen/ExpressionEmitter.h @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +//! @file ExpressionEmitter.h +//! @brief Code emitter for expression trees +//! @note Only included if slang is configured to use LLVM +// +// File is under the MIT license; see LICENSE for details +//------------------------------------------------------------------------------ +#pragma once + +#include +#include + +#include "slang/symbols/ASTVisitor.h" + +namespace slang { + +class CodeGenerator; + +class ExpressionEmitter { +public: + ExpressionEmitter(CodeGenerator& generator, llvm::BasicBlock* bb); + + llvm::Value* emit(const Expression& expr); + + // TODO: remove once all expressions are handled + template + llvm::Value* visit(const T& expr) { return getUndef(*expr.type); } + + llvm::Value* visit(const IntegerLiteral& expr); + llvm::Value* visit(const CallExpression& expr); + + llvm::Value* visitInvalid(const Expression&) { return ir.CreateUnreachable(); } + +private: + llvm::Value* getUndef(const Type& type); + + CodeGenerator& generator; + llvm::IRBuilder<> ir; +}; + +} // namespace slang \ No newline at end of file diff --git a/include/slang/codegen/StatementEmitter.h b/include/slang/codegen/StatementEmitter.h new file mode 100644 index 000000000..af74e8581 --- /dev/null +++ b/include/slang/codegen/StatementEmitter.h @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +//! @file StatementEmitter.h +//! @brief Code emitter for statements +//! @note Only included if slang is configured to use LLVM +// +// File is under the MIT license; see LICENSE for details +//------------------------------------------------------------------------------ +#pragma once + +#include +#include + +#include "slang/binding/Statements.h" + +namespace slang { + +class CodeGenerator; + +class StatementEmitter { +public: + StatementEmitter(CodeGenerator& generator, llvm::BasicBlock* bb); + + void emit(const Statement& stmt); + + // TODO: remove once all statements are handled + template + void visit(const T&) {} + + void visit(const ExpressionStatement& stmt); + + void visitInvalid(const Statement&) {} + +private: + CodeGenerator& generator; + llvm::IRBuilder<> ir; +}; + +} // namespace slang \ No newline at end of file diff --git a/include/slang/codegen/SystemCallEmitter.h b/include/slang/codegen/SystemCallEmitter.h new file mode 100644 index 000000000..ae1f24a9d --- /dev/null +++ b/include/slang/codegen/SystemCallEmitter.h @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +//! @file SystemCallEmitter.h +//! @brief Code emitter for system calls (tasks and functions) +//! @note Only included if slang is configured to use LLVM +// +// File is under the MIT license; see LICENSE for details +//------------------------------------------------------------------------------ +#pragma once + +#include +#include +#include + +#include "slang/util/Util.h" + +namespace slang { + +class CodeGenerator; +class Expression; +class Scope; +class SystemSubroutine; + +class SystemCallEmitter { +public: +private: +}; + +} // namespace slang \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index cd075e768..03027a04f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -117,7 +117,12 @@ add_library(slang ) if(SLANG_INCLUDE_LLVM) - target_sources(slang PRIVATE codegen/CodeGenerator.cpp) + target_sources(slang PRIVATE + codegen/CodeGenerator.cpp + codegen/ExpressionEmitter.cpp + codegen/StatementEmitter.cpp + codegen/SystemCallEmitter.cpp + ) endif() add_dependencies(slang gen_version) diff --git a/source/codegen/CodeGenerator.cpp b/source/codegen/CodeGenerator.cpp index 0bf9b0295..ee687de8c 100644 --- a/source/codegen/CodeGenerator.cpp +++ b/source/codegen/CodeGenerator.cpp @@ -7,19 +7,15 @@ //------------------------------------------------------------------------------ #include "slang/codegen/CodeGenerator.h" -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4996) // std::iterator deprecation warning -#endif #include #include #include #include #include -#ifdef _MSC_VER -# pragma warning(pop) -#endif +#include "slang/codegen/ExpressionEmitter.h" +#include "slang/codegen/StatementEmitter.h" +#include "slang/compilation/Compilation.h" #include "slang/symbols/ASTVisitor.h" #include "slang/symbols/Symbol.h" @@ -28,59 +24,42 @@ namespace slang { CodeGenerator::CodeGenerator(Compilation& compilation) : compilation(compilation) { ctx = std::make_unique(); module = std::make_unique("primary", *ctx); -} -CodeGenerator::~CodeGenerator() = default; + // Register built-in types. + typeMap.emplace(&compilation.getVoidType(), llvm::Type::getVoidTy(*ctx)); -std::string CodeGenerator::run(const Symbol& symbol) { - // Create a "main" function. + // Create the main entry point. auto intType = llvm::Type::getInt32Ty(*ctx); auto funcType = llvm::FunctionType::get(intType, /* isVarArg */ false); - auto mainFunc = - llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", *module); + mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", *module); - auto bb = llvm::BasicBlock::Create(*ctx, "", mainFunc); - llvm::IRBuilder<> ir(bb); - - // Declare C puts function for printing. - funcType = llvm::FunctionType::get(intType, { llvm::Type::getInt8PtrTy(*ctx) }, - /* isVarArg */ false); - auto puts = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "puts", *module); - auto callPuts = [&](string_view text) { - ir.CreateCall(puts, - { ir.CreateGlobalStringPtr(llvm::StringRef(text.data(), text.length())) }); - }; - - // Visit all procedural blocks. - symbol.visit(makeVisitor([&](const ProceduralBlockSymbol& block) { - // Only look at initial blocks. - if (block.procedureKind == ProceduralBlockKind::Initial) { - // Find all subroutine calls. - block.getBody().visit(makeVisitor([&](const CallExpression& call) { - // Only look at $display calls. - if (call.getSubroutineName() == "$display") { - // Emit a call to "puts" for every argument that has a known constant value. - for (auto arg : call.arguments()) { - EvalContext evalContext(compilation); - ConstantValue val = arg->eval(evalContext); - if (val) { - if (arg->isImplicitString()) - callPuts(val.convertToStr().toString()); - else - callPuts(val.toString()); - } - } - } - })); - } - })); - - ir.CreateRet(llvm::ConstantInt::get(intType, 0)); + // Create the first basic block that will run at the start of simulation + // to initialize all static variables. + globalInitBlock = llvm::BasicBlock::Create(*ctx, "", mainFunc); +} + +CodeGenerator::~CodeGenerator() = default; +std::string CodeGenerator::finish() { + // Insert all initial blocks into the main function. + auto lastBlock = globalInitBlock; + for (auto block : initialBlocks) { + llvm::IRBuilder<>(lastBlock).CreateBr(block); + lastBlock = block; + } + + // Finish the main function. + auto intType = llvm::Type::getInt32Ty(*ctx); + llvm::IRBuilder<>(lastBlock).CreateRet(llvm::ConstantInt::get(intType, 0)); + + // Verify all generated code. bool bad = llvm::verifyModule(*module, &llvm::errs()); - if (bad) + if (bad) { + module->dump(); return ""; + } + // Return the module bitcode. std::string result; llvm::raw_string_ostream os(result); module->print(os, nullptr); @@ -88,4 +67,124 @@ std::string CodeGenerator::run(const Symbol& symbol) { return os.str(); } +void CodeGenerator::genInstance(const InstanceSymbol& instance) { + instance.visit(makeVisitor([this](const VariableSymbol& symbol) { genGlobal(symbol); }, + [this](const ProceduralBlockSymbol& symbol) { genBlock(symbol); })); +} + +void CodeGenerator::genBlock(const ProceduralBlockSymbol& block) { + // For now skip everything except initial blocks. + if (block.procedureKind != ProceduralBlockKind::Initial) + return; + + // Create a block that will contain all of the process's statements. + auto bb = llvm::BasicBlock::Create(*ctx, "", mainFunc); + genStmt(bb, block.getBody()); + initialBlocks.push_back(bb); +} + +llvm::Type* CodeGenerator::genType(const Type& type) { + // Unwrap aliases. + if (type.isAlias()) + return genType(type.getCanonicalType()); + + // Check the cache. + if (auto it = typeMap.find(&type); it != typeMap.end()) + return it->second; + + if (!type.isIntegral()) + THROW_UNREACHABLE; + + // Underlying representation for integer types: + // - Two state types: use the bitwidth as specified + // - Four state types: double the specified bitwidth, + // the upper bits indicate a 1 for unknowns + // - If the actual width > configured limit, switch to an array of bytes + auto& intType = type.as(); + uint32_t bits = intType.bitWidth; + if (intType.isFourState) + bits *= 2; + + llvm::Type* result; + if (bits > options.maxIntBits) + result = llvm::ArrayType::get(llvm::Type::getInt64Ty(*ctx), (bits + 63) / 64); + else + result = llvm::Type::getIntNTy(*ctx, bits); + + typeMap.emplace(&type, result); + return result; +} + +llvm::Constant* CodeGenerator::genConstant(const Type& type, const ConstantValue& cv) { + // TODO: other value types + return genConstant(type, cv.integer()); +} + +llvm::Constant* CodeGenerator::genConstant(const Type& type, const SVInt& integer) { + auto& intType = type.as(); + uint32_t bits = intType.bitWidth; + if (intType.isFourState) + bits *= 2; + + llvm::ArrayRef data(integer.getRawPtr(), integer.getNumWords()); + if (bits <= options.maxIntBits) + return llvm::ConstantInt::get(*ctx, llvm::APInt(bits, data)); + else + return llvm::ConstantDataArray::get(*ctx, data); +} + +llvm::Value* CodeGenerator::genExpr(llvm::BasicBlock* bb, const Expression& expr) { + ExpressionEmitter emitter(*this, bb); + return emitter.emit(expr); +} + +void CodeGenerator::genStmt(llvm::BasicBlock* bb, const Statement& stmt) { + StatementEmitter emitter(*this, bb); + emitter.emit(stmt); +} + +void CodeGenerator::genGlobal(const VariableSymbol& variable) { + ASSERT(variable.lifetime == VariableLifetime::Static); + + auto& type = variable.getType(); + + bool needsInitializer = false; + llvm::Constant* constVal = nullptr; + if (auto init = variable.getInitializer()) { + EvalContext evCtx(compilation); + ConstantValue val = init->eval(evCtx); + if (val) + constVal = genConstant(type, val); + else + needsInitializer = true; + } + + // If no initializer provided, use the default for the type. + if (!constVal) + constVal = genConstant(type, type.getDefaultValue()); + + auto global = new llvm::GlobalVariable(*module, genType(type), /* isConstant */ false, + llvm::GlobalValue::PrivateLinkage, constVal); + globalMap.emplace(&variable, global); + + // If we set needsInitializer, the variable has an initializer expression + // but it's not constant. Emit it into the basic block that will run at + // the start of simulation. + if (needsInitializer) { + auto expr = genExpr(globalInitBlock, *variable.getInitializer()); + llvm::IRBuilder<> ir(globalInitBlock); + ir.CreateStore(expr, global); + } +} + +llvm::Function* CodeGenerator::genSubroutine(const SubroutineSymbol&) { + THROW_UNREACHABLE; +} + +llvm::Function* CodeGenerator::genSubroutine(const SystemSubroutine& subroutine) { + auto it = sysSubroutineMap.find(&subroutine); + ASSERT(it != sysSubroutineMap.end()); + return it->second; +} + } // namespace slang \ No newline at end of file diff --git a/source/codegen/ExpressionEmitter.cpp b/source/codegen/ExpressionEmitter.cpp new file mode 100644 index 000000000..02cb21957 --- /dev/null +++ b/source/codegen/ExpressionEmitter.cpp @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// ExpressionEmitter.cpp +// Code emitter for expression trees +// NOTE: Only included if slang is configured to use LLVM +// +// File is under the MIT license; see LICENSE for details +//------------------------------------------------------------------------------ +#include "slang/codegen/ExpressionEmitter.h" + +#include "slang/codegen/CodeGenerator.h" +#include "slang/symbols/ASTVisitor.h" + +namespace slang { + +ExpressionEmitter::ExpressionEmitter(CodeGenerator& generator, llvm::BasicBlock* bb) : + generator(generator), ir(bb) { +} + +llvm::Value* ExpressionEmitter::emit(const Expression& expr) { + return expr.visit(*this); +} + +llvm::Value* ExpressionEmitter::visit(const IntegerLiteral& expr) { + return generator.genConstant(*expr.type, expr.getValue()); +} + +llvm::Value* ExpressionEmitter::visit(const CallExpression& expr) { + return getUndef(*expr.type); +} + +llvm::Value* ExpressionEmitter::getUndef(const Type& type) { + return llvm::UndefValue::get(generator.genType(type)); +} + +} // namespace slang \ No newline at end of file diff --git a/source/codegen/StatementEmitter.cpp b/source/codegen/StatementEmitter.cpp new file mode 100644 index 000000000..fdbe841a6 --- /dev/null +++ b/source/codegen/StatementEmitter.cpp @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// StatementEmitter.cpp +// Code emitter for statements +// NOTE: Only included if slang is configured to use LLVM +// +// File is under the MIT license; see LICENSE for details +//------------------------------------------------------------------------------ +#include "slang/codegen/StatementEmitter.h" + +#include "slang/codegen/CodeGenerator.h" +#include "slang/symbols/ASTVisitor.h" + +namespace slang { + +StatementEmitter::StatementEmitter(CodeGenerator& generator, llvm::BasicBlock* bb) : + generator(generator), ir(bb) { +} + +void StatementEmitter::emit(const Statement& stmt) { + stmt.visit(*this); +} + +void StatementEmitter::visit(const ExpressionStatement& stmt) { + generator.genExpr(ir.GetInsertBlock(), stmt.expr); +} + +} // namespace slang \ No newline at end of file diff --git a/source/codegen/SystemCallEmitter.cpp b/source/codegen/SystemCallEmitter.cpp new file mode 100644 index 000000000..aab129cbc --- /dev/null +++ b/source/codegen/SystemCallEmitter.cpp @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// SystemCallEmitter.cpp +// Code emitter for system calls (tasks and functions) +// NOTE: Only included if slang is configured to use LLVM +// +// File is under the MIT license; see LICENSE for details +//------------------------------------------------------------------------------ +#include "slang/codegen/SystemCallEmitter.h" + +#include "slang/binding/SystemSubroutine.h" +#include "slang/codegen/CodeGenerator.h" + +namespace slang { + +} // namespace slang \ No newline at end of file diff --git a/tests/unittests/CodeGenTests.cpp b/tests/unittests/CodeGenTests.cpp index eb4729d5f..1d6f087b3 100644 --- a/tests/unittests/CodeGenTests.cpp +++ b/tests/unittests/CodeGenTests.cpp @@ -7,6 +7,7 @@ #include "slang/compilation/Compilation.h" #include "slang/diagnostics/DiagnosticEngine.h" #include "slang/symbols/CompilationUnitSymbols.h" +#include "slang/symbols/DefinitionSymbols.h" #include "slang/syntax/SyntaxTree.h" using namespace slang; @@ -29,29 +30,26 @@ TEST_CASE("Basic test") { Compilation compilation = compile(R"( module m; localparam int i = 5; - localparam real r = 3.14; + int r = 3; initial $display(i, r, "SDFSDF"); endmodule )"); CodeGenerator codegen(compilation); - auto result = codegen.run(compilation.getRoot()); + codegen.genInstance(*compilation.getRoot().topInstances[0]); + auto result = codegen.finish(); CHECK("\n" + result == R"( ; ModuleID = 'primary' source_filename = "primary" -@0 = private unnamed_addr constant [2 x i8] c"5\00", align 1 -@1 = private unnamed_addr constant [5 x i8] c"3.14\00", align 1 -@2 = private unnamed_addr constant [7 x i8] c"SDFSDF\00", align 1 +@0 = private global i32 3 define i32 @main() { - %1 = call i32 @puts(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0)) - %2 = call i32 @puts(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0)) - %3 = call i32 @puts(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @2, i32 0, i32 0)) + br label %1 + +1: ; preds = %0 ret i32 0 } - -declare i32 @puts(i8*) )"); } \ No newline at end of file