1 module samples.fibonacci;
2 
3 import std.conv : to;
4 import std.stdio : writefln, writeln;
5 import std.string : toStringz, fromStringz;
6 
7 import llvm.c;
8 
9 int main(string[] args)
10 {
11 	LLVM.load();
12 	
13 	char* error;
14 
15 	LLVMInitializeNativeTarget();
16 	LLVMInitializeNativeAsmPrinter();
17 	LLVMInitializeNativeAsmParser();
18 	
19 	auto _module = LLVMModuleCreateWithName("fibonacci".toStringz());
20 	auto f_args = [ LLVMInt32Type() ];
21 	auto f = LLVMAddFunction(
22 		_module,
23 		"fib",
24 		LLVMFunctionType(LLVMInt32Type(), f_args.ptr, 1, cast(LLVMBool) false));
25 	LLVMSetFunctionCallConv(f, LLVMCCallConv);
26 	
27 	auto n = LLVMGetParam(f, 0);
28 	
29 	auto entry = LLVMAppendBasicBlock(f, "entry".toStringz());
30 	auto case_base0 = LLVMAppendBasicBlock(f, "case_base0".toStringz());
31 	auto case_base1 = LLVMAppendBasicBlock(f, "case_base1".toStringz());
32 	auto case_default = LLVMAppendBasicBlock(f, "case_default".toStringz());
33 	auto end = LLVMAppendBasicBlock(f, "end".toStringz());
34 	auto builder = LLVMCreateBuilder();
35 	
36 	/+ Entry basic block +/
37 	LLVMPositionBuilderAtEnd(builder, entry);
38 	auto Switch = LLVMBuildSwitch(
39 		builder,
40 		n,
41 		case_default,
42 		2);
43 	LLVMAddCase(Switch, LLVMConstInt(LLVMInt32Type(), 0, cast(LLVMBool) false), case_base0);
44 	LLVMAddCase(Switch, LLVMConstInt(LLVMInt32Type(), 1, cast(LLVMBool) false), case_base1);
45 
46 	/+ Basic block for n = 0: fib(n) = 0 +/
47 	LLVMPositionBuilderAtEnd(builder, case_base0);
48 	auto res_base0 = LLVMConstInt(LLVMInt32Type(), 0, cast(LLVMBool) false);
49 	LLVMBuildBr(builder, end);
50 	
51 	/+ Basic block for n = 1: fib(n) = 1 +/
52 	LLVMPositionBuilderAtEnd(builder, case_base1);
53 	auto res_base1 = LLVMConstInt(LLVMInt32Type(), 1, cast(LLVMBool) false);
54 	LLVMBuildBr(builder, end);
55 	
56 	/+ Basic block for n > 1: fib(n) = fib(n - 1) + fib(n - 2) +/
57 	LLVMPositionBuilderAtEnd(builder, case_default);
58 
59 	auto n_minus_1 = LLVMBuildSub(
60 		builder,
61 		n,
62 		LLVMConstInt(LLVMInt32Type(), 1, cast(LLVMBool) false),
63 		"n - 1".toStringz());
64 	auto call_f_1_args = [ n_minus_1 ];
65 	auto call_f_1 = LLVMBuildCall(builder, f, call_f_1_args.ptr, 1, "fib(n - 1)".toStringz());
66 	
67 	auto n_minus_2 = LLVMBuildSub(
68 		builder,
69 		n,
70 		LLVMConstInt(LLVMInt32Type(), 2, cast(LLVMBool) false),
71 		"n - 2".toStringz());
72 	auto call_f_2_args = [ n_minus_2 ];
73 	auto call_f_2 = LLVMBuildCall(builder, f, call_f_2_args.ptr, 1, "fib(n - 2)".toStringz());
74 	
75 	auto res_default = LLVMBuildAdd(builder, call_f_1, call_f_2, "fib(n - 1) + fib(n - 2)".toStringz());
76 	LLVMBuildBr(builder, end);
77 	
78 	/+ Basic block for collecting the result +/
79 	LLVMPositionBuilderAtEnd(builder, end);
80 	auto res = LLVMBuildPhi(builder, LLVMInt32Type(), "result".toStringz());
81 	auto phi_vals = [ res_base0, res_base1, res_default ];
82 	auto phi_blocks = [ case_base0, case_base1, case_default ];
83 	LLVMAddIncoming(res, phi_vals.ptr, phi_blocks.ptr, 3);
84 	LLVMBuildRet(builder, res);
85 	
86 	LLVMVerifyModule(_module, LLVMAbortProcessAction, &error);
87 	LLVMDisposeMessage(error);
88 	
89 	LLVMExecutionEngineRef engine;
90 	error = null;
91 	
92 	version(Windows)
93 	{
94 		/+ On Windows, we can only use the old JIT for now +/
95 		LLVMCreateJITCompilerForModule(&engine, _module, 2, &error);
96 	}
97 	else
98 	{
99 		// Ran into some issues under Arch, so comment the new MVJIT out for now
100 		/+static if(LLVM_Version >= 3.3)
101 		{
102 			/+ On other systems we should be able to use the newer
103 			 + MCJIT instead - if we have a high enough LLVM version +/
104 			LLVMMCJITCompilerOptions options;
105 			LLVMInitializeMCJITCompilerOptions(&options, options.sizeof);
106 
107 			LLVMCreateMCJITCompilerForModule(&engine, _module, &options, options.sizeof, &error);
108 		}
109 		else
110 		{+/
111 			LLVMCreateJITCompilerForModule(&engine, _module, 2, &error);
112 		//}
113 	}
114 
115 	if(error !is null)
116 	{
117 		writefln("%s", error.fromStringz());
118 		LLVMDisposeMessage(error);
119 		return 1;
120 	}
121 	
122 	auto pass = LLVMCreatePassManager();
123 	LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), pass);
124 	LLVMAddConstantPropagationPass(pass);
125 	LLVMAddInstructionCombiningPass(pass);
126 	LLVMAddPromoteMemoryToRegisterPass(pass);
127 	LLVMAddGVNPass(pass);
128 	LLVMAddCFGSimplificationPass(pass);
129 	LLVMRunPassManager(pass, _module);
130 	
131 	writefln("The following module has been generated for the fibonacci series:\n");
132 	LLVMDumpModule(_module);
133 	
134 	writeln();
135 	
136 	int n_exec= 10;
137 	if(args.length > 1)
138 	{
139 		n_exec = to!int(args[1]);
140 	}
141 	else
142 	{
143 		writefln("; Argument for fib missing on command line, using default:  \"%d\"", n_exec);
144 	}
145 	
146 	auto exec_args = [ LLVMCreateGenericValueOfInt(LLVMInt32Type(), n_exec, cast(LLVMBool) 0) ];
147 	writefln("; Running (jit-compiled) fib(%d)...", n_exec);
148 	auto exec_res = LLVMRunFunction(engine, f, 1, exec_args.ptr);
149 	writefln("; fib(%d) = %d", n_exec, LLVMGenericValueToInt(exec_res, 0));
150 	
151 	LLVMDisposePassManager(pass);
152 	LLVMDisposeBuilder(builder);
153 	LLVMDisposeExecutionEngine(engine);
154 	return 0;	
155 }