Added support for intrinsics.
As access to intrinsics is not exposed by LLVM-C API, a custom C++ library
exposing those hase been added - llvm_c_ext. Smalltalk binds to this library
in addition to LLVM's.
In a future this library will expose whatever C++ API will be needed
in scope of this project. However, it's designed to usable standalone, i.e.,
it contains no Smalltalk-specic code.
"
Copyright (C) 2015-now Jan Vrany
This code is not an open-source (yet). You may use this code
for your own experiments and projects, given that:
* all modification to the code will be sent to the
original author for inclusion in future releases
* this is not used in any commercial software
This license is provisional and may (will) change in
a future.
"
"{ Package: 'jv:llvm_s' }"
"{ NameSpace: Smalltalk }"
TestCase subclass:#LLVMExamples
instanceVariableNames:''
classVariableNames:''
poolDictionaries:'LLVMIntPredicate'
category:'LLVM-S-Core-Examples'
!
!LLVMExamples class methodsFor:'documentation'!
copyright
"
Copyright (C) 2015-now Jan Vrany
This code is not an open-source (yet). You may use this code
for your own experiments and projects, given that:
* all modification to the code will be sent to the
original author for inclusion in future releases
* this is not used in any commercial software
This license is provisional and may (will) change in
a future.
"
! !
!LLVMExamples class methodsFor:'accessing'!
isTestSelector:aSelector
^ (super isTestSelector:aSelector) or:[ aSelector startsWith: 'example' ]
"Created: / 03-08-2015 / 09:25:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!LLVMExamples methodsFor:'examples'!
example1_sum
"
Creates a simple function taking two arguments (as intptr_t) and returning
their sum (as intptr_t).
"
| module functionType function asm jit externalFunction |
"/ 1) create a module to which the function would belong. A module is
"/ a set of functions and globals that are compiled at once by the MCJIT. Once
"/ module is compiled, no more methods or clobals can be added.
module := LLVMModule newWithName: testSelector.
"/ 2) Define a function within the module with type (intptr_t, intptr_t) -> intptr_t
functionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
function := module addFunctionNamed: 'sum' type: functionType.
"/ 2) To emit LLVM IR, get an IR builder for the function.
"/ The LLVMFunction>>builder returns a builder on
"/ function's entry point (basic block, strictly speaking)
asm := function builder.
asm ret: (asm add:(function parameterAt:1) _:(function parameterAt:2)).
"/ Now, the module should look like
self assert: (module dumpString =
'; ModuleID = ''example1_sum''
define i64 @sum(i64, i64) {
entry:
%2 = add i64 %0, %1
ret i64 %2
}
').
"/ 3) To compile a function (strictly speaking, whole module) at runtime,
"/ create a jit object (called ExecutionEngine in LLVM)
jit := LLVMExecutionEngine newForModule: module.
"/ 4) Finally, obtain a reference to the function. This cause
"/ the module to be closed and compiled to machine code.
externalFunction := jit externalOfFunction: function.
self assert: (externalFunction callWith: 3 with: 4) == 7
"
LLVMExamples example1_sum
"
"Created: / 17-07-2015 / 11:47:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 08-08-2015 / 04:33:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
example2_function_call
"
Creates a module with two functions, @sum and @sum_caller. The latter
calls the former.
"
| module calleeFunctionType calleeFunction callerFunctionType callerFunction asm jit externalFunction |
module := LLVMModule newWithName: testSelector.
calleeFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
calleeFunction := module addFunctionNamed: 'sum' type: calleeFunctionType.
asm := calleeFunction builder.
asm ret: (asm add:(calleeFunction parameterAt:1) _:(calleeFunction parameterAt:2)).
callerFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
callerFunction := module addFunctionNamed: 'sum_caller' type: callerFunctionType.
asm := callerFunction builder.
asm ret: (asm call: calleeFunction _: { (callerFunction parameterAt: 1) . (callerFunction parameterAt: 2) }).
self assert: (module dumpString =
'; ModuleID = ''example2_function_call''
define i64 @sum(i64, i64) {
entry:
%2 = add i64 %0, %1
ret i64 %2
}
define i64 @sum_caller(i64, i64) {
entry:
%2 = call i64 @sum(i64 %0, i64 %1)
ret i64 %2
}
').
jit := LLVMExecutionEngine newForModule: module.
externalFunction := jit externalOfFunction: callerFunction.
self assert: (externalFunction callWith: 3 with: 4) == 7.
"
LLVMExamples example2_function_call
"
"Created: / 17-07-2015 / 12:45:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 10-08-2015 / 18:58:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
example3_hello_world
"
Creates a function @main() which calls @printf() to print
a famous 'Hello World!!' message on stdout
"
| module printfFunctionType printfFunction
helloWorldString
mainFunctionType mainFunction
asm jit externalFunction |
module := LLVMModule newWithName: testSelector.
printfFunctionType := LLVMType function: { LLVMType char pointer } varargs: true returning: LLVMType int32.
printfFunction := module addFunctionNamed: 'printf' type: printfFunctionType.
helloWorldString := module addGlobalNamed: '.str' value: (LLVMConstant string: 'Hello World!!' , Character lf).
mainFunctionType := LLVMType function: #() returning: LLVMType int32.
mainFunction := module addFunctionNamed: 'main' type: mainFunctionType.
asm := mainFunction builder.
asm call: printfFunction _: { asm gep: helloWorldString at: #(0 0) }.
asm ret: (LLVMConstant sint32: 0).
"
module writeBitcodeToFile: '/tmp/main.bc'
"
jit := LLVMExecutionEngine newForModule: module.
externalFunction := jit externalOfFunction: mainFunction.
externalFunction call.
Stdout flush
"
LLVMExamples example3_hello_world
"
"Created: / 03-08-2015 / 10:28:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 10-08-2015 / 18:58:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
example4_cond
"
Creates a function @even(intpr) which returns
1 if the parameter is even or 0 if not.
"
| module
functionType function asm isOdd ifOdd ifEven jit externalFunction |
module := LLVMModule newWithName: testSelector.
functionType := LLVMType function: { LLVMType intptr } returning: LLVMType intptr.
function := module addFunctionNamed: 'even' type: functionType.
asm := function builder.
isOdd := asm icmp: (asm and: (function parameterAt: 1) _: (LLVMConstant uintptr: 1)) _: (LLVMConstant uintptr: 1) cond: LLVMIntEQ.
ifOdd := function addBasicBlock.
ifOdd builder
ret: (LLVMConstant uintptr: 0).
ifEven := function addBasicBlock.
ifEven builder
ret: (LLVMConstant uintptr: 1).
asm if: isOdd then: ifOdd else: ifEven.
jit := LLVMExecutionEngine newForModule: module.
externalFunction := jit externalOfFunction: function.
self assert: (externalFunction callWith: 10) == 1.
self assert: (externalFunction callWith: 11) == 0.
"
LLVMExamples example3_cond
"
"Created: / 08-08-2015 / 04:16:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
example5_factorial
"
S simple factorial using recursive algorithm.
No negative argument or overflow checks
"
| module
functionType function asm
"Variables" result i
"Blocks" entry loop loopBody exit
jit externalFunction |
module := LLVMModule newWithName: testSelector.
functionType := LLVMType function: { LLVMType intptr } returning: LLVMType intptr.
function := module addFunctionNamed: 'factorial' type: functionType.
asm := LLVMBuilder new.
entry := function entry.
loop := function addBasicBlockNamed: 'loop'.
loopBody := function addBasicBlockNamed: 'loopBody'.
exit := function addBasicBlockNamed: 'exit'.
"/ Generate function setup
"/
"/ function f(v) {
"/ var result;
"/ var i;
"/ result = 0;
"/ i := v;
asm block: entry.
result := asm alloca: LLVMType intptr as: 'result'.
i := asm alloca: LLVMType intptr as: 'i'.
asm store: (function parameterAt: 1) _: i.
asm store: (function parameterAt: 1) _: result.
asm br: loop.
"/ Generate loop that computes the factorial
"/
"/ while ( i > 1 ) {
"/ result = result * i;
"/ i = i - 1.
"/ }
"/
"/ Note, that unlike 'traditional' assemblers, there's no
"/ fall-through instruction, so we have to introduce a block
"/ loop's body which will become a target for conditional's
"/ then-branch.
asm block: loop.
asm if: (asm icmp: (asm load: i) _: (LLVMConstant sintptr:1) cond: LLVMIntSGT) then: loopBody else: exit.
asm block: loopBody.
asm store: (asm mul: (asm load: result) _: (asm load: i)) _: result.
asm store: (asm sub: (asm load: i) _: (LLVMConstant sintptr:1)) _: i.
asm br: loop.
"/ Generate return from function
"/
"/ return result;
"/
asm block: exit.
asm ret: (asm load: result).
jit := LLVMExecutionEngine newForModule: module.
externalFunction := jit externalOfFunction: function.
self assert: (externalFunction callWith: 5) == 120.
self assert: (externalFunction callWith: 1) == 1.
"
LLVMExamples example3_cond
"
"Created: / 10-08-2015 / 09:46:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
example5_factorial_with_overflow
"
Simple factorial using recursive algorithm.
This one checks for overflow, if overflow happens,
return -1
"
| module
functionType function asm
"Variables" result i
"Blocks" entry loop loopBody1 loopBody2 exit overflow
smulWithOverflow
smulWithOverflowValue
jit externalFunction |
module := LLVMModule newWithName: testSelector.
functionType := LLVMType function: { LLVMType intptr } returning: LLVMType intptr.
function := module addFunctionNamed: 'factorial' type: functionType.
asm := LLVMBuilder new.
entry := function entry.
loop := function addBasicBlockNamed: 'loop'.
loopBody1 := function addBasicBlockNamed: 'loopBody1'.
loopBody2 := function addBasicBlockNamed: 'loopBody2'.
exit := function addBasicBlockNamed: 'exit'.
overflow := function addBasicBlockNamed: 'overflow'.
"/ Generate function setup
"/
"/ function f(v) {
"/ var result;
"/ var i;
"/ result = 0;
"/ i := v;
asm block: entry.
result := asm alloca: LLVMType intptr as: 'result'.
i := asm alloca: LLVMType intptr as: 'i'.
asm store: (function parameterAt: 1) _: i.
asm store: (function parameterAt: 1) _: result.
asm br: loop.
"/ Generate loop that computes the factorial
"/
"/ while ( i > 1 ) {
"/ result = result * i if overflow goto overflow;
"/ i = i - 1.
"/ }
"/
"/ First, get the llvm.smul.with.overflow intrinsic:
smulWithOverflow := module getIntrinsicNamed: 'llvm.smul.with.overflow' types: { LLVMType intptr }.
"/ Now code the loop
asm block: loop.
asm if: (asm icmp: (asm load: i) _: (LLVMConstant sintptr:1) cond: LLVMIntSGT) then: loopBody1 else: exit.
asm block: loopBody1.
smulWithOverflowValue := asm call: smulWithOverflow _: { (asm load: result) . (asm load: i) }.
asm if: (asm extractvalue: smulWithOverflowValue at: 1) then: overflow else: loopBody2.
asm block: loopBody2.
asm store: (asm extractvalue: smulWithOverflowValue at: 0) _: result.
asm store: (asm sub: (asm load: i) _: (LLVMConstant sintptr:1)) _: i.
asm br: loop.
"/ Generate return from function
"/
"/ return result;
"/
asm block: exit.
asm ret: (asm load: result).
"/ Generate overflow handler
"/ overflow:
"/ return -1;
"/
asm block: overflow.
asm ret: (LLVMConstant sintptr: -1).
jit := LLVMExecutionEngine newForModule: module.
externalFunction := jit externalOfFunction: function.
self assert: (externalFunction callWith: 5) == 120.
self assert: (externalFunction callWith: 1) == 1.
self assert: (externalFunction callWith: 120) == -1.
"sly LLVMExamples example3_cond
"
"Created: / 10-08-2015 / 17:12:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 10-08-2015 / 18:58:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !