LLVMExamples.st
changeset 16 23e82cf19788
parent 14 c7dea3fcc5a7
child 18 6e80a93f4251
--- a/LLVMExamples.st	Tue Aug 04 07:21:26 2015 +0100
+++ b/LLVMExamples.st	Mon Aug 03 18:08:14 2015 +0100
@@ -15,7 +15,7 @@
 
 "{ NameSpace: Smalltalk }"
 
-Object subclass:#LLVMExamples
+TestCase subclass:#LLVMExamples
 	instanceVariableNames:''
 	classVariableNames:''
 	poolDictionaries:''
@@ -40,38 +40,85 @@
 "
 ! !
 
-!LLVMExamples class methodsFor:'examples'!
+!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 functionEntry asm jit externalFunction |
 
-    module := LLVMModule newWithName: thisContext selector.
+    "/ 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.
 
+    "/ 3) Generate function code. LLVM IR does not work with
+    "/    labels / jumps to labels but rather the user is responsible
+    "/    for creating basic blocks and adding them to the function.
+    "/    Hence, create a basic block named 'entry'
     functionEntry := function addBasicBlockNamed: 'entry'.
+
+    "/ 4) To emit LLVM IR, create an IR builder and position it
+    "/    to the end of just created basic block.
     asm := LLVMBuilder new.
     asm positionAtEnd: functionEntry.
     asm ret: (asm add: (function parameterAt: 1) and: (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
+}
+').
+
+
+    "/ 5) To compile a function (strictly speaking, whole module) at runtime,
+    "/    create a jit object (called ExecutionEngine in LLVM)
     jit := LLVMExecutionEngine newForModule: module.
+
+    "/ 6) Finally, obtain a reference to the function. This cause
+    "/    the module to be closed and compiled to machine code.
     externalFunction := jit externalOfFunction: function.
 
-    ^ externalFunction callWith: 3 with: 4.
+    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: / 03-08-2015 / 10:29:10 / 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 calleFunctionEntry 
     callerFunctionType callerFunction callerFunctionEntry
     asm jit externalFunction |
 
-    module := LLVMModule newWithName: thisContext selector.
+    module := LLVMModule newWithName: testSelector.
 
     calleeFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
     calleeFunction := module addFunctionNamed: 'sum' type: calleeFunctionType.
@@ -88,49 +135,73 @@
     asm := LLVMBuilder new.
     asm positionAtEnd: callerFunctionEntry.
     asm ret: (asm call: calleeFunction with: (callerFunction parameterAt: 1) with: (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.
 
-    ^ externalFunction callWith: 3 with: 4.
+    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: / 17-07-2015 / 17:18:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 03-08-2015 / 10:29:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
-example3_function_call
+example3_hello_world
+    "
+    Creates a function @main() which calls @printf() to print
+    a famous 'Hello World!!' message on stdout
+    "    
+
     | module printfFunctionType printfFunction  
-    callerFunctionType callerFunction callerFunctionEntry
-    asm jit externalFunction |
+     helloWorldString
+     mainFunctionType mainFunction mainFunctionEntry
+     asm jit externalFunction |
 
-    module := LLVMModule newWithName: thisContext selector.
+    module := LLVMModule newWithName: testSelector.
 
-    printfFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
-    printfFunction := module addFunctionNamed: 'sum' type: printfFunctionType.
+    printfFunctionType := LLVMType function: #() returning: LLVMType int32.
+    printfFunction := module addFunctionNamed: 'printf' type: printfFunctionType.
 
-    callerFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
-    callerFunction := module addFunctionNamed: 'sum_caller' type: callerFunctionType.
+    helloWorldString := module addGlobalNamed: '.str' value: (LLVMConstant string: 'Hello World!!\n').
 
-    callerFunctionEntry := callerFunction addBasicBlockNamed: 'entry'.
+    mainFunctionType := LLVMType function: #() returning: LLVMType intptr.
+    mainFunction := module addFunctionNamed: 'main' type: mainFunctionType.
+
+    mainFunctionEntry := mainFunction addBasicBlockNamed: 'entry'.
     asm := LLVMBuilder new.
-    asm positionAtEnd: callerFunctionEntry.
-    asm ret: (asm call: printfFunction with: (callerFunction parameterAt: 1) with: (callerFunction parameterAt: 2)).
-
+    asm positionAtEnd: mainFunctionEntry.
+    asm call: printfFunction with: helloWorldString.
+    asm ret: (LLVMConstant sint32: 0).
 
     jit := LLVMExecutionEngine newForModule: module.
-    externalFunction := jit externalOfFunction: callerFunction.
+    externalFunction := jit externalOfFunction: mainFunction.
 
-    ^ externalFunction callWith: 3 with: 4.
+    externalFunction call.
+    Stdout flush
 
     "
-    LLVMExamples example2_function_call
+    LLVMExamples example3_hello_world
     "
 
-    "Created: / 17-07-2015 / 17:21:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Created: / 03-08-2015 / 10:28:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 03-08-2015 / 18:00:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !