|

楼主 |
发表于 2010-11-21 17:36:06
|
显示全部楼层
函数代码创建
函数创建比较麻烦一些,因为要处理函数参数个数、函数名称等信息,所以代码也比较丑陋……首先是函数声明代码生成:
- '========PrototypeAST.cls
- Option Explicit
- Private Declare Function lstrlen Lib "kernel32.dll" Alias "lstrlenA" (ByRef lpString As Any) As Long
- Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
- '/// PrototypeAST - This class represents the "prototype" for a function,
- '/// which captures its name, and its argument names (thus implicitly the number
- '/// of arguments the function takes).
- Public sName As String
- Public Args As New Collection 'std::vector<std::string> Args
- Public Function CodeGen() As Long
- '// Make the function type: double(double,double) etc.
- Dim Doubles() As Long
- Dim i As Long, m As Long
- Dim FT As Long, F As Long
- m = Args.Count
- ReDim Doubles(m)
- For i = 0 To m - 1
- Doubles(i) = LLVMDoubleType
- Next i
- FT = LLVMFunctionType(LLVMDoubleType, Doubles(0), m, 0)
- 'a workaround for a stupid bug in LLVM: string can be "" but can't be vbNullString
- If sName = "" Then sName = ""
- F = LLVMAddFunction(hModule, sName, FT)
- LLVMSetLinkage F, LLVMExternalLinkage
复制代码 首先看看有几个参数,创建一个数组和参数个数一样大,里面全部填上Double类型,表示每个参数都是Double类型……然后调用LLVMFunctionType创建函数类型,函数返回值也是Double,最后一个“0”表示参数个数是固定的……
然后调用LLVMAddFunction往我们的模块里面添加一个函数……然后我发现LLVM有一个奇怪的Bug,字符串不能传vbNullString进去,要不然会非法操作……只能传""进去,所以要写一个看似没用的代码(If sName = "" Then sName = "" )来修正这个Bug……
设置链接类型为LLVMExternalLinkage说明这个函数可能在模块之外实现的(例如sin)或者可以被模块之外的代码引用……
然后判断函数是否重复定义。因为C/C++里面允许多次声明同一个函数,所以我们的山寨语言也希望能这样,不过VB是没有这个功能的 所以如果你想写一个山寨Basic,这些东西可以忽略掉 ……
- '// If F conflicted, there was already something named 'Name'. If it has a
- '// body, don't allow redefinition or reextern.
- Dim lp As Long, s As String
- lp = LLVMGetValueName(F)
- If lp <> 0 Then
- i = lstrlen(ByVal lp)
- If i > 0 Then
- s = LeftB(Space(i \ 2 + 1), i)
- CopyMemory ByVal StrPtr(s), ByVal lp, i
- s = StrConv(s, vbUnicode)
- End If
- End If
- If s <> sName Then
- '// Delete the one we just made and get the existing one.
- LLVMDeleteFunction F
- F = LLVMGetNamedFunction(hModule, sName)
复制代码 首先获得我们刚刚创建的函数名称。由于API的返回值不能是String,所以只能写Long,我们需要写一些讨厌的API调用代码,把一个字符串从Long所指向的指针里面拿出来。前面说过LLVM有一个特点:如果重复定义一个东西的话,新定义的那个会自动重命名,加上一个编号。所哟我们判断一下新的名字是不是我们原来指定的那个。如果不是的话就说明重复定义了,这时把刚刚定义的函数删掉,从已经定义的函数里找出我们想要的那个。
然后我们判断一下这个函数到底被实现过没有(这时就不能再声明了),还有参数个数是否一样:
- '// If F already has a body, reject this.
- If LLVMCountBasicBlocks(F) > 0 Then
- pError "redefinition of function"
- Exit Function
- End If
-
- '// If F took a different number of args, reject.
- If LLVMCountParams(F) <> m Then
- pError "redefinition of function with different # args"
- Exit Function
- End If
- End If
复制代码 获取当前函数的基本块数量(LLVMCountBasicBlocks),如果不是0就说明实现过了。然后判断参数个数……
最后把函数的各个参数名称填上:
- '// Set names for all arguments.
- Dim AI As Long
- For i = 0 To m - 1
- AI = LLVMGetParam(F, i)
- s = Args.Item(i + 1)
- LLVMSetValueName AI, s
-
- '// Add arguments to variable symbol table.
- On Error Resume Next
- s = StringToHex(s)
- NamedValues.Remove s
- NamedValues.Add AI, s
- On Error GoTo 0
- Next i
- CodeGen = F
- End Function
复制代码 参数名称同时被填到LLVM内部的函数参数信息里面(LLVMGetParam+LLVMSetValueName),以及我们的符号表里面(先删掉已有的,再添加新的 够山寨的吧?)
我们这个代码有一个Bug:函数的参数名可以重名,例如“extern foo(a b a)”。修正这个Bug应该不难,就留给我们的读者完成吧
接下来是函数实现的创建了,FunctionAST.cls:
- Public Function CodeGen() As Long
- Set NamedValues = New Collection
-
- Dim TheFunction As Long
- TheFunction = Proto.CodeGen
- If TheFunction = 0 Then Exit Function
复制代码 首先把符号表整个干掉。够山寨的吧?如果有全局变量等东西,这个办法就是错的了 ……然后创建函数声明的代码。如果创建失败,就直接退出去。
然后创建一个基本块。基本块就是用来放代码的,而且里面的代码总是顺序执行的。控制流不能跳到基本块的中间,只能跳到开头。而且基本块中间的代码执行到一半也不能跳出去,只能在末尾跳出去。创建好之后,把中间代码构建器的插入位置设置成我们新创建的基本块:
- '// Create a new basic block to start insertion into.
- Dim BB As Long
- BB = LLVMAppendBasicBlock(TheFunction, "entry")
- LLVMPositionBuilderAtEnd hBuilder, BB
复制代码 接下来创建代码:
- Dim RetVal As Long
- RetVal = Body.CodeGen
- If RetVal Then
- '// Finish off the function.
- LLVMBuildRet hBuilder, RetVal
- '// Validate the generated code, checking for consistency.
- LLVMVerifyFunction TheFunction, LLVMPrintMessageAction
- CodeGen = TheFunction
- Exit Function
- End If
- '// Error reading body, remove function.
- LLVMDeleteFunction TheFunction
- End Function
复制代码 先对函数体生成代码,如果生成失败的话就把当前函数删掉,退出。如果生成成功,就在我们的基本块里面添加一个返回语句,返回表达式的结果。然后调用验证函数(LLVMVerifyFunction)看看我们生成的代码有没有问题。如果你写很复杂的编译器的话可能会经常出问题哟 。
然后返回我们创建的函数,OK ……
这段代码还有一个严重的Bug:就是如果我们先声明了一个函数,然后定义它,但是定义错了,然后我们会发现这个函数不见了 :
extern foo(a b); # ok, defines foo.
def foo(a b) c; # error, 'c' is invalid.
def bar() foo(1, 2); # error, unknown function "foo"
有很多种办法修正这个Bug,你先自己想一想吧 。
[未完待续……] |
|