LLVMExamples.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Mon, 03 Aug 2015 18:08:14 +0100
changeset 16 23e82cf19788
parent 14 c7dea3fcc5a7
child 18 6e80a93f4251
permissions -rw-r--r--
Tests refactored to be actually a testcase. Introduced LLVMConstant class as a factory for LLVM constant values (such as integer or string constants)

"
    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:''
	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 functionEntry 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.

    "/ 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.

    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: testSelector.

    calleeFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
    calleeFunction := module addFunctionNamed: 'sum' type: calleeFunctionType.

    calleFunctionEntry := calleeFunction addBasicBlockNamed: 'entry'.
    asm := LLVMBuilder new.
    asm positionAtEnd: calleFunctionEntry.
    asm ret: (asm add: (calleeFunction parameterAt: 1) and: (calleeFunction parameterAt: 2)).

    callerFunctionType := LLVMType function: { LLVMType intptr . LLVMType intptr } returning: LLVMType intptr.
    callerFunction := module addFunctionNamed: 'sum_caller' type: callerFunctionType.

    callerFunctionEntry := callerFunction addBasicBlockNamed: 'entry'.
    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.

    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: / 03-08-2015 / 10:29:00 / 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 mainFunctionEntry
     asm jit externalFunction |

    module := LLVMModule newWithName: testSelector.

    printfFunctionType := LLVMType function: #() returning: LLVMType int32.
    printfFunction := module addFunctionNamed: 'printf' type: printfFunctionType.

    helloWorldString := module addGlobalNamed: '.str' value: (LLVMConstant string: 'Hello World!!\n').

    mainFunctionType := LLVMType function: #() returning: LLVMType intptr.
    mainFunction := module addFunctionNamed: 'main' type: mainFunctionType.

    mainFunctionEntry := mainFunction addBasicBlockNamed: 'entry'.
    asm := LLVMBuilder new.
    asm positionAtEnd: mainFunctionEntry.
    asm call: printfFunction with: helloWorldString.
    asm ret: (LLVMConstant sint32: 0).

    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: / 03-08-2015 / 18:00:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !