Edited code blocks, fixed links.

This commit is contained in:
Maggie Mari 2012-08-16 12:03:41 -05:00
commit c30095c77c
2 changed files with 419 additions and 341 deletions

View file

@ -13,10 +13,10 @@ with the LLVM APIs. It should also be easier to create working
prototypes and experimental languages using this medium. prototypes and experimental languages using this medium.
Together with `clang <http://clang.llvm.org/>`_ or Together with `clang <http://clang.llvm.org/>`_ or
`llvm-gcc <http://llvm.org/cmds/llvmgcc.html>`_ it also a provides a `llvm-gcc <http://llvm.org/releases/2.7/docs/CommandGuide/html/llvmgcc.html>`_
means to quickly instrument C and C++ sources. For e.g., llvm-gcc can be it also a provides a means to quickly instrument C and C++ sources. For e.g.,
used to generate the LLVM assembly for a given C source file, which can llvm-gcc can be used to generate the LLVM assembly for a given C source file,
then be loaded and manipulated (adding profiling code to every function, which can then be loaded and manipulated (adding profiling code to every function,
say) using a llvmpy based Python script. say) using a llvmpy based Python script.
License License
@ -49,7 +49,7 @@ welcome).
Installation Installation
============ ============
The Git repo of llvmpy is at https://github.com/numba/llvmpy.git. The Git repo of llvmpy is at https://github.com/llvmpy/llvmpy.git.
You'll need to build and install it before it can be used. At least the You'll need to build and install it before it can be used. At least the
following will be required for this: following will be required for this:

View file

@ -406,11 +406,12 @@ prototype. Code generation of the prototype ensures that there is an
LLVM Function object that is ready to go for us. LLVM Function object that is ready to go for us.
.. code-block:: python ::
# Create a new basic block to start insertion into. # Create a new basic block to start insertion into.
block = function.append_basic_block('entry') block = function.append_basic_block('entry')
global g_llvm_builder g_llvm_builder = Builder.new(block) global g_llvm_builder
g_llvm_builder = Builder.new(block)
@ -428,7 +429,7 @@ Graph <http://en.wikipedia.org/wiki/Control_flow_graph>`_. Since we
don't have any control flow, our functions will only contain one block don't have any control flow, our functions will only contain one block
at this point. We'll fix this in `Chapter 5 <PythonLangImpl5.html>`_ :). at this point. We'll fix this in `Chapter 5 <PythonLangImpl5.html>`_ :).
.. code-block:: python ::
# Finish off the function. # Finish off the function.
try: try:
@ -539,11 +540,15 @@ LLVM builder calls that we use to create the instructions.
.. code-block:: bash .. code-block:: bash
ready> def bar(a) foo(a, 4.0) + bar(31337) Read a ready> def bar(a) foo(a, 4.0) + bar(31337)
function definition: define double @bar(double %a) { entry: %calltmp = Read a function definition:
call double @foo(double %a, double 4.000000e+00) ; <double> [#uses=1] %calltmp1 = define double @bar(double %a) {
call double @bar(double 3.133700e+04) ; <double> [#uses=1] %addtmp = fadd double entry:
%calltmp, %calltmp1 ; <double> [#uses=1] ret double %addtmp } %calltmp = call double @foo(double %a, double 4.000000e+00) ; <double> [#uses=1]
%calltmp1 = call double @bar(double 3.133700e+04) ; <double> [#uses=1]
%addtmp = fadd double %calltmp, %calltmp1 ; <double> [#uses=1]
ret double %addtmp
}
@ -554,12 +559,17 @@ control flow to actually make recursion useful :).
.. code-block:: bash .. code-block:: bash
ready> extern cos(x) Read extern: declare double ready> extern cos(x)
@cos(double) Read extern:
declare double @cos(double)
ready> cos(1.234) Read a top-level expression: define double @1() { ready> cos(1.234)
entry: %calltmp = call double @cos(double 1.234000e+00) ; <double> [#uses=1] ret Read a top-level expression:
double %calltmp } define double @1() {
entry:
%calltmp = call double @cos(double 1.234000e+00) ; <double> [#uses=1]
ret double %calltmp
}
@ -568,26 +578,40 @@ This shows an extern for the libm "cos" function, and a call to it.
.. code-block:: bash .. code-block:: bash
ready> ^C ; ModuleID = 'my cool jit' ready> ^C
; ModuleID = 'my cool jit'
define double @0() { entry: ret double 9.000000e+00 } define double @0() {
entry:
define double @foo(double %a, double %b) { entry: %multmp = fmul double ret double 9.000000e+00
%a, %a ; <double> [#uses=1] %multmp1 = fmul double 2.000000e+00, %a ; <double> [#uses=1]
%multmp2 = fmul double %multmp1, %b ; <double> [#uses=1] %addtmp = fadd double
%multmp, %multmp2 ; <double> [#uses=1] %multmp3 = fmul double %b, %b ; <double> [#uses=1]
%addtmp4 = fadd double %addtmp, %multmp3 ; <double> [#uses=1] ret double %addtmp4
} }
define double @bar(double %a) { entry: %calltmp = call double define double @foo(double %a, double %b) {
@foo(double %a, double 4.000000e+00) ; <double> [#uses=1] %calltmp1 = call double entry:
@bar(double 3.133700e+04) ; <double> [#uses=1] %addtmp = fadd double %calltmp, %multmp = fmul double %a, %a ; <double> [#uses=1]
%calltmp1 ; <double> [#uses=1] ret double %addtmp } %multmp1 = fmul double 2.000000e+00, %a ; <double> [#uses=1]
%multmp2 = fmul double %multmp1, %b ; <double> [#uses=1]
%addtmp = fadd double %multmp, %multmp2 ; <double> [#uses=1]
%multmp3 = fmul double %b, %b ; <double> [#uses=1]
%addtmp4 = fadd double %addtmp, %multmp3 ; <double> [#uses=1]
ret double %addtmp4
}
define double @bar(double %a) {
entry:
%calltmp = call double @foo(double %a, double 4.000000e+00) ; <double> [#uses=1]
%calltmp1 = call double @bar(double 3.133700e+04) ; <double> [#uses=1]
%addtmp = fadd double %calltmp, %calltmp1 ; <double> [#uses=1]
ret double %addtmp
}
declare double @cos(double) declare double @cos(double)
define double @1() { entry: %calltmp = call double @cos(double define double @1() {
1.234000e+00) ; <double> [#uses=1] ret double %calltmp } entry:
%calltmp = call double @cos(double 1.234000e+00) ; <double> [#uses=1]
ret double %calltmp
}
@ -615,8 +639,8 @@ need to `download <../download.html>`_ and
#!/usr/bin/env python #!/usr/bin/env python
import re from llvm.core import Module, Constant, Type, Function, import re
Builder, FCMP_ULT from llvm.core import Module, Constant, Type, Function, Builder, FCMP_ULT
Globals Globals
------- -------
@ -635,31 +659,41 @@ need to `download <../download.html>`_ and
----- -----
# The lexer yields one of these types for each token. # The lexer yields one of these types for each token.
class EOFToken(object): pass class EOFToken(object):
pass
class DefToken(object): pass class DefToken(object):
pass
class ExternToken(object): pass class ExternToken(object):
pass
class IdentifierToken(object): def __init__(self, name): self.name = class IdentifierToken(object):
name def __init__(self, name):
self.name = name
class NumberToken(object): def __init__(self, value): self.value = class NumberToken(object):
value def __init__(self, value):
self.value = value
class CharacterToken(object): def __init__(self, char): self.char = class CharacterToken(object):
char def __eq__(self, other): return isinstance(other, CharacterToken) def __init__(self, char):
and self.char == other.char def __ne__(self, other): return not self self.char = char def __eq__(self, other):
== other return isinstance(other, CharacterToken)and self.char == other.char
def __ne__(self, other):
return not self == other
# Regular expressions that tokens and comments of our language. # Regular expressions that tokens and comments of our language.
REGEX_NUMBER = re.compile('[0-9]+(?:.[0-9]+)?') REGEX_IDENTIFIER = REGEX_NUMBER = re.compile('[0-9]+(?:.[0-9]+)?')
re.compile('[a-zA-Z][a-zA-Z0-9]\ *') REGEX_COMMENT = re.compile('#.*') REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]\ *')
REGEX_COMMENT = re.compile('#.*')
def Tokenize(string): while string: # Skip whitespace. if def Tokenize(string):
string[0].isspace(): string = string[1:] continue while string:
# Skip whitespace.
:: if string[0].isspace():
string = string[1:]
continue
# Run regexes. # Run regexes.
comment_match = REGEX_COMMENT.match(string) comment_match = REGEX_COMMENT.match(string)
@ -695,23 +729,29 @@ need to `download <../download.html>`_ and
------------------------------------- -------------------------------------
# Base class for all expression nodes. # Base class for all expression nodes.
class ExpressionNode(object): pass class ExpressionNode(object):
pass
# Expression class for numeric literals like "1.0". # Expression class for numeric literals like "1.0".
class NumberExpressionNode(ExpressionNode): class NumberExpressionNode(ExpressionNode):
def __init__(self, value): self.value = value def __init__(self, value):
self.value = value
def CodeGen(self): return Constant.real(Type.double(), self.value) def CodeGen(self):
return Constant.real(Type.double(), self.value)
# Expression class for referencing a variable, like "a". # Expression class for referencing a variable, like "a".
class VariableExpressionNode(ExpressionNode): class VariableExpressionNode(ExpressionNode):
def __init__(self, name): self.name = name def __init__(self, name):
self.name = name
def CodeGen(self): if self.name in g_named_values: return def CodeGen(self):
g_named_values[self.name] else: raise RuntimeError('Unknown variable if self.name in g_named_values:
name: ' + self.name) return g_named_values[self.name]
else:
raise RuntimeError('Unknown variable name: ' + self.name)
# Expression class for a binary operator. # Expression class for a binary operator.
class BinaryOperatorExpressionNode(ExpressionNode): class BinaryOperatorExpressionNode(ExpressionNode):
@ -719,10 +759,9 @@ need to `download <../download.html>`_ and
def __init__(self, operator, left, right): self.operator = operator def __init__(self, operator, left, right): self.operator = operator
self.left = left self.right = right self.left = left self.right = right
def CodeGen(self): left = self.left.CodeGen() right = def CodeGen(self):
self.right.CodeGen() left = self.left.CodeGen()
right = self.right.CodeGen()
::
if self.operator == '+': if self.operator == '+':
return g_llvm_builder.fadd(left, right, 'addtmp') return g_llvm_builder.fadd(left, right, 'addtmp')
@ -740,13 +779,13 @@ need to `download <../download.html>`_ and
# Expression class for function calls. # Expression class for function calls.
class CallExpressionNode(ExpressionNode): class CallExpressionNode(ExpressionNode):
def __init__(self, callee, args): self.callee = callee self.args = def __init__(self, callee, args):
args self.callee = callee
self.args = args
def CodeGen(self): # Look up the name in the global module table. callee def CodeGen(self):
= g_llvm_module.get_function_named(self.callee) # Look up the name in the global module table.
callee = g_llvm_module.get_function_named(self.callee)
::
# Check for argument mismatch error. # Check for argument mismatch error.
if len(callee.args) != len(self.args): if len(callee.args) != len(self.args):
@ -761,13 +800,14 @@ need to `download <../download.html>`_ and
# takes). # takes).
class PrototypeNode(object): class PrototypeNode(object):
def __init__(self, name, args): self.name = name self.args = args def __init__(self, name, args):
self.name = name
self.args = args
def CodeGen(self): # Make the function type, eg. double(double,double). def CodeGen(self):
funct_type = Type.function( Type.double(), [Type.double()] \* # Make the function type, eg. double(double,double).
len(self.args), False) funct_type = Type.function(
Type.double(), [Type.double()] * len(self.args), False)
::
function = Function.new(g_llvm_module, funct_type, self.name) function = Function.new(g_llvm_module, funct_type, self.name)
@ -797,12 +837,13 @@ need to `download <../download.html>`_ and
# This class represents a function definition itself. # This class represents a function definition itself.
class FunctionNode(object): class FunctionNode(object):
def __init__(self, prototype, body): self.prototype = prototype def __init__(self, prototype, body):
self.prototype = prototype
self.body = body self.body = body
def CodeGen(self): # Clear scope. g_named_values.clear() def CodeGen(self):
# Clear scope.
:: g_named_values.clear()
# Create a function object. # Create a function object.
function = self.prototype.CodeGen() function = self.prototype.CodeGen()
@ -830,25 +871,30 @@ need to `download <../download.html>`_ and
class Parser(object): class Parser(object):
def __init__(self, tokens, binop_precedence): self.tokens = tokens def __init__(self, tokens, binop_precedence):
self.binop_precedence = binop_precedence self.Next() self.tokens = tokens
self.binop_precedence = binop_precedence
self.Next()
# Provide a simple token buffer. Parser.current is the current token the # Provide a simple token buffer. Parser.current is the current token the
# parser is looking at. Parser.Next() reads another token from the lexer # parser is looking at. Parser.Next() reads another token from the lexer
and # updates Parser.current with its results. def Next(self): and # updates Parser.current with its results.
def Next(self):
self.current = self.tokens.next() self.current = self.tokens.next()
# Gets the precedence of the current token, or -1 if the token is not a # Gets the precedence of the current token, or -1 if the token is not a
binary # operator. def GetCurrentTokenPrecedence(self): if binary # operator.
isinstance(self.current, CharacterToken): return def GetCurrentTokenPrecedence(self):
self.binop_precedence.get(self.current.char, -1) else: return -1 if isinstance(self.current, CharacterToken):
return self.binop_precedence.get(self.current.char, -1)
else:
return -1
# identifierexpr ::= identifier \| identifier '(' expression\* ')' def # identifierexpr ::= identifier | identifier '(' expression* ')'
ParseIdentifierExpr(self): identifier_name = self.current.name def ParseIdentifierExpr(self):
identifier_name = self.current.name
self.Next() # eat identifier. self.Next() # eat identifier.
::
if self.current != CharacterToken('('): # Simple variable reference. if self.current != CharacterToken('('): # Simple variable reference.
return VariableExpressionNode(identifier_name) return VariableExpressionNode(identifier_name)
@ -867,14 +913,15 @@ need to `download <../download.html>`_ and
self.Next() # eat ')'. self.Next() # eat ')'.
return CallExpressionNode(identifier_name, args) return CallExpressionNode(identifier_name, args)
# numberexpr ::= number def ParseNumberExpr(self): result = # numberexpr ::= number
NumberExpressionNode(self.current.value) self.Next() # consume the def ParseNumberExpr(self):
number. return result result = NumberExpressionNode(self.current.value)
self.Next() # consume the number.
return result
# parenexpr ::= '(' expression ')' def ParseParenExpr(self): self.Next() # parenexpr ::= '(' expression ')'
# eat '('. def ParseParenExpr(self):
self.Next() # eat '('.
::
contents = self.ParseExpression() contents = self.ParseExpression()
@ -884,18 +931,22 @@ need to `download <../download.html>`_ and
return contents return contents
# primary ::= identifierexpr \| numberexpr \| parenexpr def # primary ::= identifierexpr | numberexpr | parenexpr
ParsePrimary(self): if isinstance(self.current, IdentifierToken): return def ParsePrimary(self):
self.ParseIdentifierExpr() elif isinstance(self.current, NumberToken): if isinstance(self.current, IdentifierToken):
return self.ParseNumberExpr() elif self.current == CharacterToken('('): return self.ParseIdentifierExpr()
return self.ParseParenExpr() else: raise RuntimeError('Unknown token elif isinstance(self.current, NumberToken):
when expecting an expression.') return self.ParseNumberExpr()
elif self.current == CharacterToken('('):
return self.ParseParenExpr()
else:
raise RuntimeError('Unknown token when expecting an expression.')
# binoprhs ::= (operator primary)\* def ParseBinOpRHS(self, left, # binoprhs ::= (operator primary)*
left_precedence): # If this is a binary operator, find its precedence. def ParseBinOpRHS(self, left, left_precedence):
while True: precedence = self.GetCurrentTokenPrecedence() # If this is a binary operator, find its precedence.
while True:
:: precedence = self.GetCurrentTokenPrecedence()
# If this is a binary operator that binds at least as tightly as the # If this is a binary operator that binds at least as tightly as the
# current one, consume it; otherwise we are done. # current one, consume it; otherwise we are done.
@ -917,14 +968,15 @@ need to `download <../download.html>`_ and
# Merge left/right. # Merge left/right.
left = BinaryOperatorExpressionNode(binary_operator, left, right) left = BinaryOperatorExpressionNode(binary_operator, left, right)
# expression ::= primary binoprhs def ParseExpression(self): left = # expression ::= primary binoprhs
self.ParsePrimary() return self.ParseBinOpRHS(left, 0) def ParseExpression(self):
left = self.ParsePrimary()
return self.ParseBinOpRHS(left, 0)
# prototype ::= id '(' id\* ')' def ParsePrototype(self): if not # prototype ::= id '(' id\* ')'
isinstance(self.current, IdentifierToken): raise RuntimeError('Expected def ParsePrototype(self):
function name in prototype.') if not isinstance(self.current, IdentifierToken):
raise RuntimeError('Expected function name in prototype.')
::
function_name = self.current.name function_name = self.current.name
self.Next() # eat function name. self.Next() # eat function name.
@ -946,39 +998,63 @@ need to `download <../download.html>`_ and
return PrototypeNode(function_name, arg_names) return PrototypeNode(function_name, arg_names)
# definition ::= 'def' prototype expression def ParseDefinition(self): # definition ::= 'def' prototype expression
self.Next() # eat def. proto = self.ParsePrototype() body = def ParseDefinition(self):
self.ParseExpression() return FunctionNode(proto, body) self.Next() # eat def.
proto = self.ParsePrototype()
body = self.ParseExpression()
return FunctionNode(proto, body)
# toplevelexpr ::= expression def ParseTopLevelExpr(self): proto = # toplevelexpr ::= expression
PrototypeNode('', []) return FunctionNode(proto, self.ParseExpression()) def ParseTopLevelExpr(self):
proto = PrototypeNode('', [])
return FunctionNode(proto, self.ParseExpression())
# external ::= 'extern' prototype def ParseExtern(self): self.Next() # # external ::= 'extern' prototype
eat extern. return self.ParsePrototype() def ParseExtern(self):
self.Next() # eat extern.
return self.ParsePrototype()
# Top-Level parsing def HandleDefinition(self): # Top-Level parsing
def HandleDefinition(self):
self.Handle(self.ParseDefinition, 'Read a function definition:') self.Handle(self.ParseDefinition, 'Read a function definition:')
def HandleExtern(self): self.Handle(self.ParseExtern, 'Read an extern:') def HandleExtern(self):
self.Handle(self.ParseExtern, 'Read an extern:')
def HandleTopLevelExpression(self): self.Handle(self.ParseTopLevelExpr, def HandleTopLevelExpression(self):
'Read a top-level expression:') self.Handle(self.ParseTopLevelExpr, 'Read a top-level expression:')
def Handle(self, function, message): try: print message, def Handle(self, function, message):
function().CodeGen() except Exception, e: print 'Error:', e try: try:
self.Next() # Skip for error recovery. except: pass print message, function().CodeGen()
except Exception, e:
print 'Error:', e
try:
self.Next() # Skip for error recovery.
except:
pass
Main driver code. Main driver code.
----------------- -----------------
def main(): # Install standard binary operators. # 1 is lowest possible def main():
precedence. 40 is the highest. operator_precedence = { '<': 10, '+': # Install standard binary operators.
20, '-': 20, '\*': 40 } # 1 is lowest possible precedence. 40 is the highest.
operator_precedence = {
'<': 10,
'+': 20,
'-': 20,
'*': 40
}
# Run the main "interpreter loop". while True: print 'ready>', try: raw # Run the main "interpreter loop".
= raw_input() except KeyboardInterrupt: break while True:
print 'ready>',
:: try:
raw = raw_input()
except KeyboardInterrupt:
break
parser = Parser(Tokenize(raw), operator_precedence) parser = Parser(Tokenize(raw), operator_precedence)
while True: while True:
@ -992,6 +1068,8 @@ need to `download <../download.html>`_ and
else: else:
parser.HandleTopLevelExpression() parser.HandleTopLevelExpression()
# Print out all of the generated code. print '', g_llvm_module # Print out all of the generated code.
print '\n', g_llvm_module
if **name** == '__main__': main() if __name__ == '__main__':
main()