| 121 |
147 |
|
setReg(&mut regTypes[..], param.value, regTypeFromIl(param.type)); |
| 122 |
148 |
|
} |
| 123 |
149 |
|
|
| 124 |
150 |
|
|
| 125 |
151 |
|
for instr, instrIdx in block.instrs { |
| 126 |
|
- |
verifyInstr( |
| 127 |
|
- |
func, blockIdx as u32, instrIdx as u32, |
| 128 |
|
- |
instr, &mut regTypes[..], errors, wtpCount, a, |
| 129 |
|
- |
); |
|
152 |
+ |
let mut ctx = VerifyCtx { |
|
153 |
+ |
func, |
|
154 |
+ |
blockIdx: blockIdx as u32, |
|
155 |
+ |
instrIdx: instrIdx as u32, |
|
156 |
+ |
regs: &mut regTypes[..], |
|
157 |
+ |
errors, |
|
158 |
+ |
a, |
|
159 |
+ |
}; |
|
160 |
+ |
verifyInstr(&mut ctx, instr, wtpCount); |
| 130 |
161 |
|
} |
| 131 |
162 |
|
} |
| 132 |
163 |
|
} |
| 133 |
164 |
|
|
| 134 |
165 |
|
|
| 135 |
|
- |
fn verifyInstr( |
| 136 |
|
- |
func: *super::Fn, |
| 137 |
|
- |
blockIdx: u32, |
| 138 |
|
- |
instrIdx: u32, |
| 139 |
|
- |
instr: super::Instr, |
| 140 |
|
- |
regs: *mut [RegType], |
| 141 |
|
- |
errors: *mut *mut [VerifyError], |
| 142 |
|
- |
wtpCount: *mut u32, |
| 143 |
|
- |
a: alloc::Allocator, |
| 144 |
|
- |
) { |
|
166 |
+ |
fn verifyInstr(ctx: *mut VerifyCtx, instr: super::Instr, wtpCount: *mut u32) { |
| 145 |
167 |
|
match instr { |
| 146 |
|
- |
case super::Instr::Reserve { dst, .. } => { |
| 147 |
|
- |
|
| 148 |
|
- |
setReg(regs, dst, RegType::Ptr); |
|
168 |
+ |
case super::Instr::Reserve { dst, size, .. } => { |
|
169 |
+ |
let bound = immVal(size) else { |
|
170 |
+ |
setReg(ctx.regs, dst, RegType::Ptr(UNKNOWN_BOUND)); |
|
171 |
+ |
return; |
|
172 |
+ |
}; |
|
173 |
+ |
setReg(ctx.regs, dst, RegType::Ptr(bound as u32)); |
| 149 |
174 |
|
} |
| 150 |
|
- |
case super::Instr::Load { typ, dst, src, .. } => { |
| 151 |
|
- |
checkPtrBase(func, blockIdx, instrIdx, regs, src, errors, a, "load base"); |
| 152 |
|
- |
setReg(regs, dst, regTypeFromIl(typ)); |
|
175 |
+ |
case super::Instr::Load { typ, dst, src, offset } => { |
|
176 |
+ |
checkPtrBase(ctx, src, "load base"); |
|
177 |
+ |
checkAccess(ctx, src, offset, super::typeSize(typ)); |
|
178 |
+ |
setReg(ctx.regs, dst, regTypeFromIl(typ)); |
| 153 |
179 |
|
} |
| 154 |
|
- |
case super::Instr::Sload { typ, dst, src, .. } => { |
| 155 |
|
- |
checkPtrBase(func, blockIdx, instrIdx, regs, src, errors, a, "sload base"); |
| 156 |
|
- |
setReg(regs, dst, regTypeFromIl(typ)); |
|
180 |
+ |
case super::Instr::Sload { typ, dst, src, offset } => { |
|
181 |
+ |
checkPtrBase(ctx, src, "sload base"); |
|
182 |
+ |
checkAccess(ctx, src, offset, super::typeSize(typ)); |
|
183 |
+ |
setReg(ctx.regs, dst, regTypeFromIl(typ)); |
| 157 |
184 |
|
} |
| 158 |
|
- |
case super::Instr::Store { dst, .. } => { |
| 159 |
|
- |
checkPtrBase(func, blockIdx, instrIdx, regs, dst, errors, a, "store base"); |
|
185 |
+ |
case super::Instr::Store { typ, dst, offset, .. } => { |
|
186 |
+ |
checkPtrBase(ctx, dst, "store base"); |
|
187 |
+ |
checkAccess(ctx, dst, offset, super::typeSize(typ)); |
| 160 |
188 |
|
} |
| 161 |
|
- |
case super::Instr::Blit { dst, src, .. } => { |
| 162 |
|
- |
checkPtrBase(func, blockIdx, instrIdx, regs, dst, errors, a, "blit dst"); |
| 163 |
|
- |
checkPtrBase(func, blockIdx, instrIdx, regs, src, errors, a, "blit src"); |
|
189 |
+ |
case super::Instr::Blit { dst, src, size } => { |
|
190 |
+ |
checkPtrBase(ctx, dst, "blit dst"); |
|
191 |
+ |
checkPtrBase(ctx, src, "blit src"); |
|
192 |
+ |
if let sz = immVal(size) { |
|
193 |
+ |
checkAccess(ctx, dst, 0, sz as u32); |
|
194 |
+ |
checkAccess(ctx, src, 0, sz as u32); |
|
195 |
+ |
} |
| 164 |
196 |
|
} |
| 165 |
197 |
|
case super::Instr::Copy { dst, val } => { |
| 166 |
|
- |
|
| 167 |
|
- |
let vt = resolveValType(val, regs); |
| 168 |
|
- |
setReg(regs, dst, vt); |
|
198 |
+ |
setReg(ctx.regs, dst, resolveValType(val, ctx.regs)); |
| 169 |
199 |
|
} |
| 170 |
200 |
|
case super::Instr::BinOp { op, typ, dst, a: va, b: vb } => { |
| 171 |
201 |
|
if let case super::Type::Ptr = typ { |
| 172 |
|
- |
|
| 173 |
|
- |
|
| 174 |
|
- |
let aIsPtr = isPtr(resolveValType(va, regs)); |
| 175 |
|
- |
let bIsPtr = isPtr(resolveValType(vb, regs)); |
|
202 |
+ |
let aIsPtr = isPtr(resolveValType(va, ctx.regs)); |
|
203 |
+ |
let bIsPtr = isPtr(resolveValType(vb, ctx.regs)); |
| 176 |
204 |
|
|
| 177 |
205 |
|
if let case super::BinOp::Add = op { |
| 178 |
206 |
|
if not (aIsPtr or bIsPtr) { |
| 179 |
|
- |
emitError(func, blockIdx, instrIdx, errors, a, |
|
207 |
+ |
emitError(ctx.func, ctx.blockIdx, ctx.instrIdx, ctx.errors, ctx.a, |
| 180 |
208 |
|
"ptr add: neither operand is Ptr"); |
| 181 |
209 |
|
} |
| 182 |
210 |
|
} |
| 183 |
|
- |
setReg(regs, dst, RegType::Ptr); |
|
211 |
+ |
setReg(ctx.regs, dst, RegType::Ptr(derivePtrAdd(va, vb, ctx.regs))); |
| 184 |
212 |
|
} else { |
| 185 |
|
- |
setReg(regs, dst, RegType::Word(typ)); |
|
213 |
+ |
setReg(ctx.regs, dst, RegType::Word(typ)); |
| 186 |
214 |
|
} |
| 187 |
215 |
|
} |
| 188 |
|
- |
case super::Instr::UnOp { typ, dst, .. } => { |
| 189 |
|
- |
setReg(regs, dst, RegType::Word(typ)); |
| 190 |
|
- |
} |
| 191 |
|
- |
case super::Instr::Zext { dst, .. } => { |
| 192 |
|
- |
setReg(regs, dst, RegType::Word(super::Type::W64)); |
| 193 |
|
- |
} |
| 194 |
|
- |
case super::Instr::Sext { dst, .. } => { |
| 195 |
|
- |
setReg(regs, dst, RegType::Word(super::Type::W64)); |
| 196 |
|
- |
} |
| 197 |
|
- |
case super::Instr::PtrToWord { dst, .. } => { |
| 198 |
|
- |
setReg(regs, dst, RegType::Word(super::Type::W64)); |
| 199 |
|
- |
} |
|
216 |
+ |
case super::Instr::UnOp { typ, dst, .. } => |
|
217 |
+ |
setReg(ctx.regs, dst, RegType::Word(typ)), |
|
218 |
+ |
case super::Instr::Zext { dst, .. } => |
|
219 |
+ |
setReg(ctx.regs, dst, RegType::Word(super::Type::W64)), |
|
220 |
+ |
case super::Instr::Sext { dst, .. } => |
|
221 |
+ |
setReg(ctx.regs, dst, RegType::Word(super::Type::W64)), |
|
222 |
+ |
case super::Instr::PtrToWord { dst, .. } => |
|
223 |
+ |
setReg(ctx.regs, dst, RegType::Word(super::Type::W64)), |
| 200 |
224 |
|
case super::Instr::WordToPtr { dst, .. } => { |
| 201 |
|
- |
|
| 202 |
225 |
|
*wtpCount += 1; |
| 203 |
|
- |
setReg(regs, dst, RegType::Ptr); |
|
226 |
+ |
setReg(ctx.regs, dst, RegType::Ptr(UNKNOWN_BOUND)); |
| 204 |
227 |
|
} |
| 205 |
228 |
|
case super::Instr::Call { retTy, dst, .. } => { |
| 206 |
229 |
|
if let d = dst { |
| 207 |
|
- |
setReg(regs, d, regTypeFromIl(retTy)); |
|
230 |
+ |
setReg(ctx.regs, d, regTypeFromIl(retTy)); |
| 208 |
231 |
|
} |
| 209 |
232 |
|
} |
| 210 |
|
- |
case super::Instr::Ecall { dst, .. } => { |
| 211 |
|
- |
|
| 212 |
|
- |
setReg(regs, dst, RegType::Word(super::Type::W64)); |
| 213 |
|
- |
} |
|
233 |
+ |
case super::Instr::Ecall { dst, .. } => |
|
234 |
+ |
setReg(ctx.regs, dst, RegType::Word(super::Type::W64)), |
| 214 |
235 |
|
case super::Instr::Elem { dst, base, .. } => { |
| 215 |
|
- |
|
| 216 |
|
- |
checkPtrBase(func, blockIdx, instrIdx, regs, base, errors, a, "elem base"); |
| 217 |
|
- |
setReg(regs, dst, RegType::Ptr); |
|
236 |
+ |
checkPtrBase(ctx, base, "elem base"); |
|
237 |
+ |
setReg(ctx.regs, dst, RegType::Ptr(UNKNOWN_BOUND)); |
| 218 |
238 |
|
} |
| 219 |
239 |
|
case super::Instr::Ret { .. }, |
| 220 |
240 |
|
super::Instr::Jmp { .. }, |
| 221 |
241 |
|
super::Instr::Br { .. }, |
| 222 |
242 |
|
super::Instr::Switch { .. }, |
| 223 |
243 |
|
super::Instr::Unreachable, |
| 224 |
244 |
|
super::Instr::Ebreak => {} |
| 225 |
245 |
|
} |
| 226 |
246 |
|
} |
| 227 |
247 |
|
|
|
248 |
+ |
|
|
249 |
+ |
|
|
250 |
+ |
|
|
251 |
+ |
fn derivePtrAdd(va: super::Val, vb: super::Val, regs: *[RegType]) -> u32 { |
|
252 |
+ |
|
|
253 |
+ |
let aType = resolveValType(va, regs); |
|
254 |
+ |
let bType = resolveValType(vb, regs); |
|
255 |
+ |
|
|
256 |
+ |
let mut bound: u32 = UNKNOWN_BOUND; |
|
257 |
+ |
let mut offset: ?i64 = nil; |
|
258 |
+ |
|
|
259 |
+ |
if isPtr(aType) { |
|
260 |
+ |
bound = ptrBound(aType); |
|
261 |
+ |
offset = immVal(vb); |
|
262 |
+ |
} else if isPtr(bType) { |
|
263 |
+ |
bound = ptrBound(bType); |
|
264 |
+ |
offset = immVal(va); |
|
265 |
+ |
} |
|
266 |
+ |
if bound == UNKNOWN_BOUND { |
|
267 |
+ |
return UNKNOWN_BOUND; |
|
268 |
+ |
} |
|
269 |
+ |
let off = offset else return UNKNOWN_BOUND; |
|
270 |
+ |
if off < 0 or off as u32 >= bound { |
|
271 |
+ |
return UNKNOWN_BOUND; |
|
272 |
+ |
} |
|
273 |
+ |
return bound - (off as u32); |
|
274 |
+ |
} |
|
275 |
+ |
|
| 228 |
276 |
|
|
| 229 |
277 |
|
fn setReg(regs: *mut [RegType], reg: super::Reg, typ: RegType) { |
| 230 |
278 |
|
if reg.n < regs.len { |
| 231 |
279 |
|
regs[reg.n] = typ; |
| 232 |
280 |
|
} |
| 233 |
281 |
|
} |
| 234 |
282 |
|
|
| 235 |
|
- |
|
| 236 |
|
- |
fn regTypeFromIl(typ: super::Type) -> RegType { |
| 237 |
|
- |
if let case super::Type::Ptr = typ { |
| 238 |
|
- |
return RegType::Ptr; |
|
283 |
+ |
|
|
284 |
+ |
fn checkPtrBase(ctx: *mut VerifyCtx, reg: super::Reg, context: *[u8]) { |
|
285 |
+ |
if reg.n >= ctx.regs.len { |
|
286 |
+ |
return; |
|
287 |
+ |
} |
|
288 |
+ |
if not isPtr(ctx.regs[reg.n]) { |
|
289 |
+ |
emitError(ctx.func, ctx.blockIdx, ctx.instrIdx, ctx.errors, ctx.a, context); |
| 239 |
290 |
|
} |
| 240 |
|
- |
return RegType::Word(typ); |
| 241 |
291 |
|
} |
| 242 |
292 |
|
|
| 243 |
|
- |
|
| 244 |
|
- |
fn checkPtrBase( |
|
293 |
+ |
|
|
294 |
+ |
|
|
295 |
+ |
|
|
296 |
+ |
fn checkAccess( |
|
297 |
+ |
ctx: *mut VerifyCtx, |
|
298 |
+ |
base: super::Reg, |
|
299 |
+ |
offset: i32, |
|
300 |
+ |
size: u32, |
|
301 |
+ |
) { |
|
302 |
+ |
if base.n >= ctx.regs.len { |
|
303 |
+ |
return; |
|
304 |
+ |
} |
|
305 |
+ |
let bound = ptrBound(ctx.regs[base.n]); |
|
306 |
+ |
if bound == UNKNOWN_BOUND { |
|
307 |
+ |
return; |
|
308 |
+ |
} |
|
309 |
+ |
if offset < 0 { |
|
310 |
+ |
emitError(ctx.func, ctx.blockIdx, ctx.instrIdx, ctx.errors, ctx.a, "negative offset"); |
|
311 |
+ |
return; |
|
312 |
+ |
} |
|
313 |
+ |
let end = (offset as u32) + size; |
|
314 |
+ |
if end > bound { |
|
315 |
+ |
emitError(ctx.func, ctx.blockIdx, ctx.instrIdx, ctx.errors, ctx.a, "access out of bounds"); |
|
316 |
+ |
} |
|
317 |
+ |
} |
|
318 |
+ |
|
|
319 |
+ |
|
|
320 |
+ |
record VerifyCtx { |
| 245 |
321 |
|
func: *super::Fn, |
| 246 |
322 |
|
blockIdx: u32, |
| 247 |
323 |
|
instrIdx: u32, |
| 248 |
|
- |
regs: *[RegType], |
| 249 |
|
- |
reg: super::Reg, |
|
324 |
+ |
regs: *mut [RegType], |
| 250 |
325 |
|
errors: *mut *mut [VerifyError], |
| 251 |
326 |
|
a: alloc::Allocator, |
| 252 |
|
- |
context: *[u8], |
| 253 |
|
- |
) { |
| 254 |
|
- |
if reg.n >= regs.len { |
| 255 |
|
- |
return; |
| 256 |
|
- |
} |
| 257 |
|
- |
if not isPtr(regs[reg.n]) { |
| 258 |
|
- |
emitError(func, blockIdx, instrIdx, errors, a, context); |
| 259 |
|
- |
} |
| 260 |
327 |
|
} |
| 261 |
328 |
|
|
| 262 |
329 |
|
|
| 263 |
330 |
|
fn emitError( |
| 264 |
331 |
|
func: *super::Fn, |