-
Notifications
You must be signed in to change notification settings - Fork 5
/
llvmtcl.tcl
369 lines (356 loc) · 12.3 KB
/
llvmtcl.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
namespace eval llvmtcl {
namespace export *
namespace ensemble create
namespace eval DebugInfo {
namespace export *
namespace ensemble create
}
proc OptimizeModule {m optimizeLevel targetDataRef} {
set pm [llvmtcl CreatePassManager]
llvmtcl AddTargetData $targetDataRef $pm
set bld [llvmtcl PassManagerBuilderCreate]
llvmtcl PassManagerBuilderSetOptLevel $bld $optimizeLevel
llvmtcl PassManagerBuilderSetDisableUnrollLoops $bld [expr {$optimizeLevel == 0}]
if {$optimizeLevel > 1} {
llvmtcl PassManagerBuilderSetDisableUnrollLoops $bld 0
llvmtcl PassManagerBuilderUseInlinerWithThreshold $bld [expr {$optimizeLevel > 2 ? 275 : 225 }]
}
llvmtcl PassManagerBuilderPopulateModulePassManager $bld $pm
llvmtcl RunPassManager $pm $m
llvmtcl DisposePassManager $pm
}
proc OptimizeFunction {m f optimizeLevel targetDataRef} {
set fpm [llvmtcl CreateFunctionPassManagerForModule $m]
llvmtcl AddTargetData $targetDataRef $fpm
set bld [llvmtcl PassManagerBuilderCreate]
llvmtcl PassManagerBuilderSetOptLevel $bld $optimizeLevel
llvmtcl PassManagerBuilderSetDisableUnrollLoops $bld [expr {$optimizeLevel == 0}]
if {$optimizeLevel > 1} {
llvmtcl PassManagerBuilderSetDisableUnrollLoops $bld 0
llvmtcl PassManagerBuilderUseInlinerWithThreshold $bld [expr {$optimizeLevel > 2 ? 275 : 225 }]
}
llvmtcl PassManagerBuilderPopulateFunctionPassManager $bld $fpm
llvmtcl InitializeFunctionPassManager $fpm
llvmtcl RunFunctionPassManager $fpm $f
llvmtcl FinalizeFunctionPassManager $fpm
llvmtcl DisposePassManager $fpm
}
proc Optimize {m funcList} {
set td [llvmtcl CreateTargetData ""]
llvmtcl SetDataLayout $m [llvmtcl CopyStringRepOfTargetData $td]
foreach f $funcList {
llvmtcl OptimizeFunction $m $f 3 $td
}
llvmtcl OptimizeModule $m 3 $td
}
proc Execute {m f args} {
llvmtcl SetTarget $m X86
set td [llvmtcl CreateTargetData "e"]
llvmtcl SetDataLayout $m [llvmtcl CopyStringRepOfTargetData $td]
lassign [llvmtcl CreateExecutionEngineForModule $m] rt EE msg
set largs {}
foreach arg $args {
lappend largs [llvmtcl CreateGenericValueOfInt [llvmtcl Int32Type] $arg 0]
}
set rt [llvmtcl GenericValueToInt [llvmtcl RunFunction $EE $f $largs] 0]
}
proc Tcl2LLVM {m procName {functionDeclarationOnly 0}} {
variable tstp
variable ts
variable tsp
variable funcar
variable utils_added
if {![info exists utils_added($m)] || !$utils_added($m)} {
AddTcl2LLVMUtils $m
}
# Disassemble the proc
set dasm [split [tcl::unsupported::disassemble proc $procName] \n]
# Create builder
set bld [llvmtcl CreateBuilder]
# Create function
if {![info exists funcar($m,$procName)]} {
set argl {}
foreach l $dasm {
set l [string trim $l]
if {[regexp {slot \d+, .*arg, \"} $l]} {
lappend argl [llvmtcl Int32Type]
}
}
set ft [llvmtcl FunctionType [llvmtcl Int32Type] $argl 0]
set func [llvmtcl AddFunction $m $procName $ft]
set funcar($m,$procName) $func
}
if {$functionDeclarationOnly} {
return $funcar($m,$procName)
}
set func $funcar($m,$procName)
# Create basic blocks
set block(0) [llvmtcl AppendBasicBlock $func "block0"]
set next_is_ipath -1
foreach l $dasm {
set l [string trim $l]
if {![string match "(*" $l]} { continue }
set opcode [lindex $l 1]
if {$next_is_ipath >= 0} {
regexp {\((\d+)\) } $l -> pc
if {![info exists block($pc)]} {
set block($pc) [llvmtcl AppendBasicBlock $func "block$pc"]
}
set ipath($next_is_ipath) $pc
set next_is_ipath -1
}
if {[string match "jump*1" $opcode] || [string match "jump*4" $opcode] || [string match "startCommand" $opcode]} {
# (pc) opcode offset
regexp {\((\d+)\) (jump\S*[14]|startCommand) (\+*\-*\d+)} $l -> pc cmd offset
set tgt [expr {$pc + $offset}]
if {![info exists block($tgt)]} {
set block($tgt) [llvmtcl AppendBasicBlock $func "block$tgt"]
}
set next_is_ipath $pc
}
}
llvmtcl PositionBuilderAtEnd $bld $block(0)
set curr_block $block(0)
# Create stack and stack pointer
set tstp [llvmtcl PointerType [llvmtcl Int8Type] 0]
set at [llvmtcl ArrayType [llvmtcl PointerType [llvmtcl Int8Type] 0] 1000]
set ts [llvmtcl BuildArrayAlloca $bld $at [llvmtcl ConstInt [llvmtcl Int32Type] 1 0] ""]
set tsp [llvmtcl BuildAlloca $bld [llvmtcl Int32Type] ""]
llvmtcl BuildStore $bld [llvmtcl ConstInt [llvmtcl Int32Type] 0 0] $tsp
# Load arguments into llvm, allocate space for slots
set n 0
foreach l $dasm {
set l [string trim $l]
if {[regexp {slot \d+, .*arg, \"} $l]} {
set arg_1 [llvmtcl GetParam $func $n]
set arg_2 [llvmtcl BuildAlloca $bld [llvmtcl Int32Type] ""]
set arg_3 [llvmtcl BuildStore $bld $arg_1 $arg_2]
set vars($n) $arg_2
incr n
} elseif {[string match "slot *" $l]} {
set arg_2 [llvmtcl BuildAlloca $bld [llvmtcl Int32Type] ""]
set vars($n) $arg_2
}
}
# Convert Tcl parse output
set LLVMBuilder2(bitor) BuildXor
set LLVMBuilder2(bitxor) BuildOr
set LLVMBuilder2(bitand) BuildAnd
set LLVMBuilder2(lshift) BuildShl
set LLVMBuilder2(rshift) BuildAShr
set LLVMBuilder2(add) BuildAdd
set LLVMBuilder2(sub) BuildSub
set LLVMBuilder2(mult) BuildMul
set LLVMBuilder2(div) BuildSDiv
set LLVMBuilder2(mod) BuildSRem
set LLVMBuilder1(uminus) BuildNeg
set LLVMBuilder1(bitnot) BuildNot
set LLVMBuilderICmp(eq) LLVMIntEQ
set LLVMBuilderICmp(neq) LLVMIntNE
set LLVMBuilderICmp(lt) LLVMIntSLT
set LLVMBuilderICmp(gt) LLVMIntSGT
set LLVMBuilderICmp(le) LLVMIntSLE
set LLVMBuilderICmp(ge) LLVMIntGE
set done_done 0
foreach l $dasm {
#puts $l
set l [string trim $l]
if {![string match "(*" $l]} { continue }
regexp {\((\d+)\) (\S+)} $l -> pc opcode
if {[info exists block($pc)]} {
llvmtcl PositionBuilderAtEnd $bld $block($pc)
set curr_block $block($pc)
set done_done 0
}
set ends_with_jump($curr_block) 0
unset -nocomplain tgt
if {[string match "jump*1" $opcode] || [string match "jump*4" $opcode] || [string match "startCommand" $opcode]} {
regexp {\(\d+\) (jump\S*[14]|startCommand) (\+*\-*\d+)} $l -> cmd offset
set tgt [expr {$pc + $offset}]
}
if {[info exists LLVMBuilder1($opcode)]} {
push $bld [llvmtcl $LLVMBuilder1($opcode) $bld [pop $bld [llvmtcl Int32Type]] ""]
} elseif {[info exists LLVMBuilder2($opcode)]} {
set top0 [pop $bld [llvmtcl Int32Type]]
set top1 [pop $bld [llvmtcl Int32Type]]
push $bld [llvmtcl $LLVMBuilder2($opcode) $bld $top1 $top0 ""]
} elseif {[info exists LLVMBuilderICmp($opcode)]} {
set top0 [pop $bld [llvmtcl Int32Type]]
set top1 [pop $bld [llvmtcl Int32Type]]
push $bld [llvmtcl BuildIntCast $bld [llvmtcl BuildICmp $bld $LLVMBuilderICmp($opcode) $top1 $top0 ""] [llvmtcl Int32Type] ""]
} else {
switch -exact -- $opcode {
"loadScalar1" {
set var $vars([string range [lindex $l 2] 2 end])
push $bld [llvmtcl BuildLoad $bld $var ""]
}
"storeScalar1" {
set var_1 [top $bld [llvmtcl Int32Type]]
set idx [string range [lindex $l 2] 2 end]
if {[info exists vars($idx)]} {
set var_2 $vars($idx)
} else {
set var_2 [llvmtcl BuildAlloca $bld [llvmtcl Int32Type] ""]
}
set var_3 [llvmtcl BuildStore $bld $var_1 $var_2]
set vars($idx) $var_2
}
"incrScalar1" {
set var $vars([string range [lindex $l 2] 2 end])
llvmtcl BuildStore $bld [llvmtcl BuildAdd $bld [llvmtcl BuildLoad $bld $var ""] [top $bld [llvmtcl Int32Type]] ""] $var
}
"incrScalar1Imm" {
set var $vars([string range [lindex $l 2] 2 end])
set i [lindex $l 3]
set s [llvmtcl BuildAdd $bld [llvmtcl BuildLoad $bld $var ""] [llvmtcl ConstInt [llvmtcl Int32Type] $i 0] ""]
push $bld $s
llvmtcl BuildStore $bld $s $var
}
"push1" {
set tval [lindex $l 4]
if {[string is integer -strict $tval]} {
set val [llvmtcl ConstInt [llvmtcl Int32Type] $tval 0]
} elseif {[info exists funcar($m,$tval)]} {
set val $funcar($m,$tval)
} else {
set val [llvmtcl ConstInt [llvmtcl Int32Type] 0 0]
}
push $bld $val
}
"jumpTrue4" -
"jumpTrue1" {
set top [pop $bld [llvmtcl Int32Type]]
if {[llvmtcl GetIntTypeWidth [llvmtcl TypeOf $top]] == 1} {
set cond $top
} else {
set cond [llvmtcl BuildICmp $bld LLVMIntNE $top [llvmtcl ConstInt [llvmtcl Int32Type] 0 0] ""]
}
llvmtcl BuildCondBr $bld $cond $block($tgt) $block($ipath($pc))
set ends_with_jump($curr_block) 1
}
"jumpFalse4" -
"jumpFalse1" {
set top [pop $bld [llvmtcl Int32Type]]
if {[llvmtcl GetIntTypeWidth [llvmtcl TypeOf $top]] == 1} {
set cond $top
} else {
set cond [llvmtcl BuildICmp $bld LLVMIntNE $top [llvmtcl ConstInt [llvmtcl Int32Type] 0 0] ""]
}
llvmtcl BuildCondBr $bld $cond $block($ipath($pc)) $block($tgt)
set ends_with_jump($curr_block) 1
}
"tryCvtToNumeric" {
push $bld [pop $bld [llvmtcl Int32Type]]
}
"startCommand" {
}
"jump4" -
"jump1" {
llvmtcl BuildBr $bld $block($tgt)
set ends_with_jump($curr_block) 1
}
"invokeStk1" {
set objc [lindex $l 2]
set objv {}
set argl {}
for {set i 0} {$i < ($objc-1)} {incr i} {
lappend objv [pop $bld [llvmtcl Int32Type]]
lappend argl [llvmtcl Int32Type]
}
set objv [lreverse $objv]
set ft [llvmtcl PointerType [llvmtcl FunctionType [llvmtcl Int32Type] $argl 0] 0]
set fptr [pop $bld $ft]
push $bld [llvmtcl BuildCall $bld $fptr $objv ""]
}
"pop" {
pop $bld [llvmtcl Int32Type]
}
"done" {
if {!$done_done} {
llvmtcl BuildRet $bld [top $bld [llvmtcl Int32Type]]
set ends_with_jump($curr_block) 1
set done_done 1
}
}
"nop" {
}
default {
error "unknown bytecode '$opcode' in '$l'"
}
}
}
}
# Set increment paths
foreach {pc b} [array get block] {
llvmtcl PositionBuilderAtEnd $bld $block($pc)
if {![info exists ends_with_jump($block($pc))] || !$ends_with_jump($block($pc))} {
set tpc [expr {$pc+1}]
while {$tpc < 1000} {
if {[info exists block($tpc)]} {
llvmtcl BuildBr $bld $block($tpc)
break
}
incr tpc
}
}
}
# Cleanup and return
llvmtcl DisposeBuilder $bld
return $func
}
# Helper functions, should not be called directly
proc AddTcl2LLVMUtils {m} {
variable funcar
variable utils_added
set bld [llvmtcl CreateBuilder]
set ft [llvmtcl FunctionType [llvmtcl Int32Type] [list [llvmtcl Int32Type]] 0]
set func [llvmtcl AddFunction $m "llvm_mathfunc_int" $ft]
set funcar($m,tcl::mathfunc::int) $func
set block [llvmtcl AppendBasicBlock $func "block"]
llvmtcl PositionBuilderAtEnd $bld $block
llvmtcl BuildRet $bld [llvmtcl GetParam $func 0]
llvmtcl DisposeBuilder $bld
set utils_added($m) 1
}
proc push {bld val} {
variable tstp
variable ts
variable tsp
# Allocate space for value
set valt [llvmtcl TypeOf $val]
set valp [llvmtcl BuildAlloca $bld $valt "push"]
llvmtcl BuildStore $bld $val $valp
# Store location on stack
set tspv [llvmtcl BuildLoad $bld $tsp "push"]
set tsl [llvmtcl BuildGEP $bld $ts [list [llvmtcl ConstInt [llvmtcl Int32Type] 0 0] $tspv] "push"]
llvmtcl BuildStore $bld [llvmtcl BuildPointerCast $bld $valp $tstp ""] $tsl
# Update stack pointer
set tspv [llvmtcl BuildAdd $bld $tspv [llvmtcl ConstInt [llvmtcl Int32Type] 1 0] "push"]
llvmtcl BuildStore $bld $tspv $tsp
}
proc pop {bld valt} {
variable ts
variable tsp
# Get location from stack and decrement the stack pointer
set tspv [llvmtcl BuildLoad $bld $tsp "pop"]
set tspv [llvmtcl BuildAdd $bld $tspv [llvmtcl ConstInt [llvmtcl Int32Type] -1 0] "pop"]
llvmtcl BuildStore $bld $tspv $tsp
set tsl [llvmtcl BuildGEP $bld $ts [list [llvmtcl ConstInt [llvmtcl Int32Type] 0 0] $tspv] "pop"]
set valp [llvmtcl BuildLoad $bld $tsl "pop"]
# Load value
set pc [llvmtcl BuildPointerCast $bld $valp [llvmtcl PointerType $valt 0] "pop"]
set rt [llvmtcl BuildLoad $bld $pc "pop"]
return $rt
}
proc top {bld valt {offset 0}} {
variable ts
variable tsp
# Get location from stack
set tspv [llvmtcl BuildLoad $bld $tsp "top"]
set tspv [llvmtcl BuildAdd $bld $tspv [llvmtcl ConstInt [llvmtcl Int32Type] -1 0] "top"]
set tsl [llvmtcl BuildGEP $bld $ts [list [llvmtcl ConstInt [llvmtcl Int32Type] 0 0] $tspv] "top"]
set valp [llvmtcl BuildLoad $bld $tsl "top"]
# Load value
return [llvmtcl BuildLoad $bld [llvmtcl BuildPointerCast $bld $valp [llvmtcl PointerType $valt 0] "top"] "top"]
}
}