-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcreateLoop.R
274 lines (229 loc) · 8.57 KB
/
createLoop.R
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
compile.for = compileForLoop =
function(call, env, ir, ..., nextBlock = NULL, .targetType = NULL, breakBlock = NULL, nextIterBlock = NULL)
{
env$.loopStack = c("for", env$.loopStack)
on.exit(env$.loopStack <- env$.loopStack[-1])
var = as.character(call[[2]])
inn = call[[3]]
isSeq = isSequence(inn)
ans = list(var = var, body = call[[4]])
if(isSeq) {
ans$limits = getLimits(inn)
} else {
# we have a call to something and so
# may need to create a temporary variable
# Then we need to loop over those elements
# and so create limits from that object, i.e.
# 1 to length(var).
# if the inn is a symbol, then we loop over its elements.
# We need to know its type.
if(!is.name(inn)) {
# create temporary variable by evaluating the call
# and then create the limits for that.
# compile the call and assign to a new variable, then
# loop over that.
tmpVar = "tmp"
ans$tempVar = structure(inn, name = tmpVar)
ans$limits = list(from = 1L, to = substitute(length(x), list(x = as.name(tmpVar))))
} else
ans$limits = list(from = 1L, to = substitute(length(x), list(x = call[[3]])))
#XXX since isSeq is FALSE here, we have to introduce a new temporary variable
# for the counter variable and rewrite the body of the loop or assign the value
# to the temporary variable. e.g. for(val in x) ... would become for(i in seq(along = x)) { val = x[i]; body }
# or else we have to change references to val to x[val]. For debugging, rewriting can be aggrevating.
# Introducing a new variable could conflict with another in the function, so need to know all of those.
if(class(ans$body) != "{")
ans$body = substitute({foo}, list(foo = ans$body))
b = ans$body
b[(2:length(body)) + 1L] = b[(2:length(body))]
e = quote(x <- y)
e[[2]] = as.name(ans$var)
ans$var = "i" #XXX pick an unused variable name
warning("<fix> compute name for counter in loop so that it is unique")
e[[3]] = substitute(y[i], list(y = inn))
b[[2]] = e
ans$body = b
}
class(ans) = "ForLoop"
createLoopCode(ans$var, ans$limits, ans$body, env, , ir = ir, nextBlock = nextBlock, label = deparse(call), ..., breakBlock = NULL, nextIterBlock = NULL)
ans
}
createLoopCode =
#
# This takes a variable, a length variable and builds a loop.
#
#
function(var, limits, body, env, fun = env$.fun, ir = IRBuilder(module), module = NULL, nextBlock = NULL,
label = ".", zeroBased = FALSE, ..., breakBlock = NULL, nextIterBlock = NULL)
{
env$.loopDepth = env$.loopDepth + 1L
on.exit( env$.loopDepth <- env$.loopDepth - 1L)
# The caller (compileFunction and compileExpressions) has already created a block
# for this expression, so we can use it as the entry block and create and initialize
# variables here.
# We do create blocks for the condition and the body.
cond = Block(fun, sprintf("cond.%s", label))
incrBlock = Block(fun, sprintf("incr.%s", label))
bodyBlock = Block(fun, sprintf("body.%s", label))
if(is.null(nextBlock)) {
#cat("[createLoopCode] creating nextBlock\n") ; browser()
nextBlock = Block(fun, sprintf("after.%s", label))
}
# pushNextBlock(env, nextBlock)
# on.exit(popNextBlock(env))
# pushContinueBlock(env, incrBlock)
# on.exit(popContinueBlock(env))
# replace each set of 3 lines with a call to createCompilerLocalVariable
iv = createFunctionVariable(Int32Type, var, env, ir) # ir$createLocalVariable(Int32Type, var)
assign(var, iv, env)
env$.types[[var]] = Int32Type
len = createFunctionVariable(Int32Type, ".llen", env, ir) # ir$createLocalVariable(Int32Type, ".llen")
assign(".llen", len, env)
env$.types[[".llen"]] = Int32Type
###
mapply(function(lim, to) {
if(is.symbol(lim)) {
sym = as.character(lim)
var = getVariable(sym, env, ir)
# ir$createLoad(to, get(as.character(lim), env))
ir$createStore(var, to)
} else if(is.call(lim) && length(lim) == 2 && as.character(lim[[1]]) == "length") {
ty = getTypes(lim[[2]], env)
if(is(ty, "SEXPType")) {
# declare Rf_length()
R.length = declareFunction(getBuiltInRoutines(env = env)[["length"]], "Rf_length", env$.module)
sym = as.character(lim[[2]])
var = getVariable(sym, env, ir)
#XXXXXX
env$addCallInfo("Rf_length")
ir$createStore(ir$createCall(R.length, var), to)
} else
stop("Not certain what to do with ", paste(deparse(lim), collaspe = " "), " for loop extents")
} else {
ir$createStore(lim, to)
}
if(zeroBased) {
# offset the loop vars by one, since LLVM is 0-indexed and R is
# 1-indexed. This is a bit clunky, but LLVM will optimize this
# all away.
offset = ir$createLoad(to)
offset.var = ir$binOp(BinaryOps['Sub'], offset, 1L)
ir$createStore(offset.var, to)
}
}, limits, list(iv, len))
ir$createBr(cond)
ir$setInsertPoint(cond)
a = ir$createLoad(iv)
b = ir$createLoad(len)
ok = ir$createICmp(ICMP_SLE, a, b)
ir$createCondBr(ok, bodyBlock, nextBlock)
#MOVED TO HERE
#If we fill in this incrementing the counter block before compiling the body of the code
# we don't get into the same trouble with if() expressions in the body not knowing where
# to jump to and jumping here and adding a branch in this incrementing block when they shouldn't.
ir$setInsertPoint(incrBlock)
i = ir$createLoad(iv)
inc = ir$binOp(BinaryOps['Add'], i, 1L)
ir$createStore(inc, iv)
ir$createBr(cond)
#END MOVED
ir$setInsertPoint(bodyBlock)
#XXX have to put the code for the actual body, not just the incrementing of i
compile(body, env, ir, ..., nextBlock = incrBlock, breakBlock = nextBlock, nextIterBlock = incrBlock)
if(!identical(ir$getInsertBlock(), incrBlock) && length(getTerminator(ir$getInsertBlock())) == 0) {
# cat("In createLoop: browser()\n")
# It is possible that the compile() has put us into the incrBlock in which case we don't want to add a Branch.
ir$createBr(incrBlock)
}
ir$setInsertPoint(nextBlock)
}
## offsetIndex <-
## # Offset indexing by one to account for LLVM 0-based indexing and R's
## # 1-based indexing
## function(x) {
## browser()
## # TODO is this safe? could there be a name clash with y?
## x$from <- substitute(expression(y - 1), list(y=x$from))
## x$to <- substitute(expression(y - 1), list(y=x$to))
## x
## }
getLimits =
# 1:10
# 1:length(x)
# seq(1, 10)
# seq(1, 10, by = 3)
# seq(1, 10, length = 2)
# seq.int(0, 10, 2)
# seq.int(0, 10, length = 3)
#
# seq_along(x)
# getLimits(quote(seq(0, 2^n, length = 2)))
# getLimits(quote(seq(0, length(x), length = 2)))
#
function(call)
{
op = as.character(call[[1]])
ans = if(op == ":") {
# TODO fix compiling of these calls
## list(from = compile(call[[2]], env, ir, ...),
## to = compile(call[[3]], env, ir, ...))
list(from = call[[2]], to = call[[3]])
} else if(op == "seq_along") {
tmp = substitute(length(x), list(x = call[[2]]))
list(from = 1L, to = tmp)
} else if(op == "seq") {
k = match.call(seq, call)
argNames = names(k)[-1]
# formals(seq) returns ...
formals = c("from", "to", "by", "length.out", "along.with")
i = argNames == ""
argNames[i] = formals[which(i)]
structure(as.list(call[-1]), names = argNames)
warning("TODO this may not work with R to LLVM indexing offsetting.")
}
## # LLVM uses 0 indexing and R doesn't; this we offset by 1
## ans <- offsetIndex(ans)
# how should we get integers when we have, e.g., 1:10 which are
# numeric
ans = lapply(ans, function(val) if(is.numeric(val) && val == as.integer(val)) as.integer(val) else val)
ans
}
isSequence =
function(expr)
{
if(is.call(expr)) {
op = expr[[1]]
if(as.character(op) %in% c("seq", ":", "seq_len", "seq.int", "seq_along"))
return(TRUE)
}
FALSE
}
# Made these all noOPs for now to see if it makes any difference
pushNextBlock =
function(env, block)
{
return(NULL)
env$.nextBlock = c(block, env$.nextBlock)
}
popNextBlock =
function(env)
{
return(NULL)
ans = env$.nextBlock[[1]]
env$.nextBlock = env$.nextBlock[-1]
ans
}
pushContinueBlock =
function(env, block)
{
return(NULL)
env$.continueBlock = c(block, env$.continueBlock)
}
popContinueBlock =
function(env)
{
return(NULL)
ans = env$.continueBlock[[1]]
env$.continueBlock = env$.continueBlock[-1]
ans
}