Skip to content

Commit

Permalink
Add optimization support for LLVM bindings (#479)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Feb 28, 2024
1 parent 3956922 commit 768fea1
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 5 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 -d -O0 -ir ../../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="build -d -O0 -g --static ../../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
59 changes: 57 additions & 2 deletions media/test-project/test.spice
@@ -1,7 +1,62 @@
import "std/data/map";
import "bootstrap/bindings/llvm/llvm" as llvm;
import "std/data/vector";

f<int> main() {
Map<int, string> map;
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);
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);
}

/*import "bootstrap/util/block-allocator";
Expand Down
120 changes: 119 additions & 1 deletion src-bootstrap/bindings/llvm/llvm.spice
Expand Up @@ -389,6 +389,16 @@ public type LLVMVerifierFailureAction enum {
ReturnStatusAction = 2
}

// ===== Additional helper enums, that are not included in llvm-c =====
public type OptimizationLevel enum {
O0 = 0,
O1 = 1,
O2 = 2,
O3 = 3,
Os = 4,
Oz = 5
}

// ===== External function declarations =====
ext f<LLVMContextRef> LLVMContextCreate();
ext p LLVMContextDispose(LLVMContextRef /*C*/);
Expand Down Expand Up @@ -474,20 +484,25 @@ ext p LLVMPassBuilderOptionsSetLicmMssaNoAccForPromotionCap(LLVMPassBuilderOptio
ext p LLVMPassBuilderOptionsSetCallGraphProfile(LLVMPassBuilderOptionsRef /*Options*/, LLVMBool /*Enable*/);
ext p LLVMPassBuilderOptionsSetMergeFunctions(LLVMPassBuilderOptionsRef /*Options*/, LLVMBool /*Enable*/);
ext p LLVMPassBuilderOptionsSetInlinerThreshold(LLVMPassBuilderOptionsRef /*Options*/, int /*InlinerThreshold*/);
ext f<LLVMErrorRef> LLVMRunPasses(LLVMModuleRef /*M*/, LLVMTargetMachineRef /*TM*/, string /*Passes*/, LLVMPassBuilderOptionsRef /*Options*/);
ext f<LLVMErrorRef> LLVMRunPasses(LLVMModuleRef /*M*/, string /*Passes*/, LLVMTargetMachineRef /*TM*/, LLVMPassBuilderOptionsRef /*Options*/);
ext p LLVM_InitializeNativeTarget();
ext p LLVM_InitializeNativeAsmParser();
ext p LLVM_InitializeNativeAsmPrinter();
ext p LLVM_InitializeNativeDisassembler();
ext p LLVM_InitializeAllTargetInfos();
ext p LLVM_InitializeAllTargets();
ext p LLVM_InitializeAllTargetMCs();
ext p LLVM_InitializeAllAsmParsers();
ext p LLVM_InitializeAllAsmPrinters();
ext p LLVM_InitializeAllDisassemblers();
ext f<heap string> LLVMGetDefaultTargetTriple();
ext f<heap string> LLVMNormalizeTargetTriple(string /*TripleIn*/);
ext f<heap string> LLVMGetHostCPUName();
ext f<heap string> LLVMGetHostCPUFeatures();
ext f<LLVMBool> LLVMGetTargetFromTriple(string /*Triple*/, LLVMTargetMachineRef* /*OutMachine*/, string* /*OutError*/);
ext f<LLVMTargetMachineRef> LLVMCreateTargetMachine(LLVMTargetRef /*T*/, heap string /*Triple*/, string /*CPU*/, string /*Features*/, LLVMCodeGenOptLevel /*OptLevel*/, LLVMRelocMode /*Reloc*/, LLVMCodeModel /*CodeModel*/);
ext p LLVMDisposeTargetMachine(LLVMTargetMachineRef /*TM*/);
ext f<LLVMBool> LLVMTargetMachineEmitToFile(LLVMTargetMachineRef /*T*/, LLVMModuleRef /*M*/, string /*Filename*/, LLVMCodeGenFileType /*codegen*/, string* /*ErrorMessage*/);
ext f<LLVMTargetDataRef> LLVMCreateTargetDataLayout(LLVMTargetMachineRef /*TM*/);
ext p LLVMDisposeTargetData(LLVMTargetDataRef /*TD*/);
ext p LLVMSetDataLayout(LLVMModuleRef /*M*/, LLVMTargetDataRef /*DL*/);
Expand Down Expand Up @@ -935,10 +950,20 @@ public type TargetMachine struct {
LLVMTargetMachineRef self
}

p TargetMachine.ctor(LLVMTargetRef targetRef, heap string triple, string cpu, string features, LLVMCodeGenOptLevel optLevel, LLVMRelocMode relocMode, LLVMCodeModel codeModel) {
this.self = LLVMCreateTargetMachine(targetRef, triple, cpu, features, optLevel, relocMode, codeModel);
}

public p TargetMachine.dtor() {
LLVMDisposeTargetMachine(this.self);
}

public f<string> TargetMachine.emitToFile(const Module& module, string filename, LLVMCodeGenFileType codegen) {
string errorMessage = "";
assert !LLVMTargetMachineEmitToFile(this.self, module.self, filename, codegen, &errorMessage);
return errorMessage;
}

public f<DataLayout> TargetMachine.createDataLayout() {
return DataLayout{ LLVMCreateTargetDataLayout(this.self) };
}
Expand Down Expand Up @@ -1001,6 +1026,71 @@ public p PassBuilderOptions.setInlinerThreshold(int threshold) {
LLVMPassBuilderOptionsSetInlinerThreshold(this.internalOptions, threshold);
}

// ===== PassInfoMixin =====
public type PassInfo interface {
f<string> getOption();
}

// ===== AlwaysInlinerPass =====
public type AlwaysInlinerPass struct : PassInfo {}

public p AlwaysInlinerPass.ctor() {}

f<string> AlwaysInlinerPass.getOption() {
return "always-inline";
}

// ===== PassBuilder =====
public type PassBuilder struct {
LLVMPassBuilderOptionsRef internalOptions
Vector<String> passes
String pipelineDescription // Textual representation of the passes to run (like for passing to opt -passes=...)
}

public p PassBuilder.ctor(const PassBuilderOptions& options) {
this.internalOptions = options.internalOptions;
}

public p PassBuilder.addPass(const String& pass) {
this.passes.pushBack(pass);
}

public p PassBuilder.addPass(string pass) {
this.passes.pushBack(String(pass));
}

// ToDo: Uncomment if this is working
/*public p PassBuilder.addPass(const PassInfo& pass) {
this.passes.pushBack(String(pass.getOption()));
}*/

// ToDo: Delete if the code above is working
public p PassBuilder.addPass(const AlwaysInlinerPass& pass) {
this.passes.pushBack(String(pass.getOption()));
}

public p PassBuilder.clearPasses() {
this.passes.clear();
}

public p PassBuilder.buildCustomPipeline() {
this.pipelineDescription.clear();
for (unsigned int i = 0; i < this.passes.getSize(); i++) {
if (i > 0) { this.pipelineDescription += ','; }
this.pipelineDescription += this.passes.get(i);
}
}

public p PassBuilder.buildPerModuleDefaultPipeline(OptimizationLevel optLevel) {
this.clearPasses();
this.addPass("default<" + getOptLevelNameFromOptLevel(optLevel) + ">");
this.buildCustomPipeline();
}

public p PassBuilder.run(const Module& module, const TargetMachine& targetMachine) {
LLVMRunPasses(module.self, this.pipelineDescription.getRaw(), targetMachine.self, this.internalOptions);
}

// ===== Static functions =====

public f<Type> getFunctionType(Type returnType, const Vector<Type>& paramTypes, bool isVarArg = false) {
Expand All @@ -1024,10 +1114,18 @@ public p initializeNativeTarget() {
LLVM_InitializeNativeTarget();
}

public p initializeNativeAsmParser() {
LLVM_InitializeNativeAsmParser();
}

public p initializeNativeAsmPrinter() {
LLVM_InitializeNativeAsmPrinter();
}

public p initializeNativeDisassembler() {
LLVM_InitializeNativeDisassembler();
}

public p initializeAllTargetInfos() {
LLVM_InitializeAllTargetInfos();
}
Expand All @@ -1040,10 +1138,18 @@ public p initializeAllTargetMCs() {
LLVM_InitializeAllTargetMCs();
}

public p initializeAllAsmParsers() {
LLVM_InitializeAllAsmParsers();
}

public p initializeAllAsmPrinters() {
LLVM_InitializeAllAsmPrinters();
}

public p initializeAllDisassemblers() {
LLVM_InitializeAllDisassemblers();
}

public f<heap string> getDefaultTargetTriple() {
return LLVMGetDefaultTargetTriple();
}
Expand All @@ -1066,6 +1172,18 @@ public f<Target> getTargetFromTriple(string triple, string* outError) {
return target;
}

public f<string> getOptLevelNameFromOptLevel(OptimizationLevel optLevel) {
switch optLevel {
case OptimizationLevel::O0: { return "O0"; }
case OptimizationLevel::O1: { return "O1"; }
case OptimizationLevel::O2: { return "O2"; }
case OptimizationLevel::O3: { return "O3"; }
case OptimizationLevel::Os: { return "Os"; }
case OptimizationLevel::Oz: { return "Oz"; }
}
return "Invalid optimization level";
}

public p getVersion(unsigned int* major, unsigned int* minor, unsigned int* patch) {
LLVMGetVersion(major, minor, patch);
}
Expand Down
@@ -1,3 +1,4 @@
Unoptimized IR:
; ModuleID = 'test'
source_filename = "test"

Expand All @@ -9,3 +10,19 @@ define i32 @main() {
}

declare i32 @printf(ptr, i32, ...)
Optimized IR:
; ModuleID = 'test'
source_filename = "test"

@str = private unnamed_addr constant [14 x i8] c"Hello, world!\00", align 1

; Function Attrs: nofree nounwind
define noundef i32 @main() local_unnamed_addr #0 {
%puts = tail call i32 @puts(ptr nonnull dereferenceable(1) @str)
ret i32 0
}

; Function Attrs: nofree nounwind
declare noundef i32 @puts(ptr nocapture noundef readonly) local_unnamed_addr #0

attributes #0 = { nofree nounwind }
Expand Up @@ -46,5 +46,15 @@ f<int> main() {
string output;
assert !llvm::verifyModule(module, &output);

printf("%s", module.print());
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);
}

0 comments on commit 768fea1

Please sign in to comment.