compiler: Select RV64 calls in checked code
b166fdc965ab805c053474a1233bcd55ab152a379a491b64767e65c6ed4c17cc
1 parent
d3ace8fb
lib/std/arch/rv64/bounds.rad
+28 -4
| 228 | 228 | il::Instr::DeviceWrite { typ: il::Type::W8, handle: a, offset: b, value: a }, |
|
| 229 | 229 | il::Instr::Ebreak, |
|
| 230 | 230 | il::Instr::MemoryFence, |
|
| 231 | 231 | ]; |
|
| 232 | 232 | for instr in instructions { |
|
| 233 | - | try checkSelectionCapacity(instr); |
|
| 233 | + | try checkSelectionCapacity(instr, nil); |
|
| 234 | 234 | } |
|
| 235 | 235 | } |
|
| 236 | 236 | ||
| 237 | - | /// Verify exact-fit output and canaries around every shorter code buffer. |
|
| 238 | - | unsafe fn checkSelectionCapacity(instr: il::Instr) throws (testing::TestError) { |
|
| 237 | + | /// Direct and indirect calls check argument and output capacities. |
|
| 238 | + | @test unsafe fn callSelectionCapacity() throws (testing::TestError) { |
|
| 239 | + | let a = il::Val::Reg(il::Reg { n: 0 }); |
|
| 240 | + | let b = il::Val::Reg(il::Reg { n: 1 }); |
|
| 241 | + | let args = [b, a, b, a, il::Val::Imm(7), a, b, a, a]; |
|
| 242 | + | for callee in [il::Val::FnAddr("p::callee"), a] { |
|
| 243 | + | for dst in [nil as ?il::Reg, il::Reg { n: 3 }] { |
|
| 244 | + | for count in [0 as u32, 8, 9] { |
|
| 245 | + | let instr = il::Instr::Call { |
|
| 246 | + | retTy: il::Type::W64, dst, func: callee, args: &args[..count], |
|
| 247 | + | }; |
|
| 248 | + | let expected: ?super::Error = super::Error::Capacity if count == 9 else nil; |
|
| 249 | + | try checkSelectionCapacity(instr, expected); |
|
| 250 | + | } |
|
| 251 | + | } |
|
| 252 | + | } |
|
| 253 | + | } |
|
| 254 | + | ||
| 255 | + | /// Verify instruction errors, exact-fit output, and shorter-buffer canaries. |
|
| 256 | + | unsafe fn checkSelectionCapacity(instr: il::Instr, expectedError: ?super::Error) throws (testing::TestError) { |
|
| 239 | 257 | let mut body = [instr, il::Instr::Ret { val: il::Val::Imm(0) }]; |
|
| 240 | 258 | let mut count: u32 = 2; |
|
| 241 | 259 | if let case il::Instr::Ret { .. } = instr { |
|
| 242 | 260 | set count = 1; |
|
| 243 | 261 | } |
| 252 | 270 | }; |
|
| 253 | 271 | let mut arena = alloc::new(&mut MEMORY[..]); |
|
| 254 | 272 | let mut gen = generator(&mut arena); |
|
| 255 | 273 | let mut scratch = alloc::new(&mut SCRATCH[..]); |
|
| 256 | 274 | super::generateFunction(&mut gen, &func, &mut scratch); |
|
| 257 | - | assert gen.e.error == nil; |
|
| 275 | + | assert gen.e.error == expectedError; |
|
| 276 | + | if expectedError <> nil { |
|
| 277 | + | let count = gen.e.codeLen; |
|
| 278 | + | emit::emit(&mut gen.e, encode::nop()); |
|
| 279 | + | assert gen.e.codeLen == count; |
|
| 280 | + | return; |
|
| 281 | + | } |
|
| 258 | 282 | let length = gen.e.codeLen; |
|
| 259 | 283 | let mut expected: [u32; 128] = [0; 128]; |
|
| 260 | 284 | assert length > 0 and length <= expected.len; |
|
| 261 | 285 | for i in 0..length { |
|
| 262 | 286 | set expected[i] = gen.e.code[i]; |
lib/std/arch/rv64/isel.rad
+39 -31
| 507 | 507 | } |
|
| 508 | 508 | // Fall through to default. |
|
| 509 | 509 | emitBlockArgs(s, func.blocks[defaultTarget].params, defaultArgs); |
|
| 510 | 510 | emit::recordBranch(s.e, defaultTarget, emit::BranchKind::Jump); |
|
| 511 | 511 | }, |
|
| 512 | - | case il::Instr::Call { retTy, dst, func, args } => { |
|
| 513 | - | // For indirect calls, save target to scratch register before arg |
|
| 514 | - | // setup can clobber it. |
|
| 515 | - | if let case il::Val::Reg(r) = func { |
|
| 516 | - | let target = getSrcReg(s, r, super::SCRATCH2); |
|
| 517 | - | emitMv(s, super::SCRATCH2, target); |
|
| 518 | - | } |
|
| 519 | - | // Move arguments to A0-A7 using parallel move resolution. |
|
| 520 | - | if args.len > super::ARG_REGS.len { |
|
| 521 | - | set s.e.error = super::Error::Capacity; |
|
| 522 | - | return; |
|
| 523 | - | } |
|
| 524 | - | emitParallelMoves(s, &super::ARG_REGS[..], args); |
|
| 512 | + | case il::Instr::Call { dst, func, args, .. } => selectCall(s, &func, args, dst), |
|
| 513 | + | else => selectFixedInstr(s, blockIdx, instr, frame), |
|
| 514 | + | } |
|
| 515 | + | } |
|
| 525 | 516 | ||
| 526 | - | // Emit call. |
|
| 527 | - | match func { |
|
| 528 | - | case il::Val::FnAddr(name) => { |
|
| 529 | - | emit::recordCall(s.e, name); |
|
| 530 | - | }, |
|
| 531 | - | case il::Val::Reg(_) => { |
|
| 532 | - | emit::emit(s.e, encode::jalr(super::RA, super::SCRATCH2, 0)); |
|
| 533 | - | }, |
|
| 534 | - | else => { |
|
| 535 | - | panic "selectInstr: invalid call target"; |
|
| 536 | - | } |
|
| 537 | - | } |
|
| 538 | - | // Move result from A0. |
|
| 539 | - | if let d = dst { |
|
| 540 | - | let rd = getDstReg(s, d, super::SCRATCH1); |
|
| 541 | - | emitMv(s, rd, super::A0); |
|
| 542 | - | } |
|
| 517 | + | /// Select a call from its target, borrowed arguments, and optional result. |
|
| 518 | + | fn selectCall 'scratch 'selection ( |
|
| 519 | + | s: &mut Selector 'scratch 'selection, |
|
| 520 | + | func: &il::Val, |
|
| 521 | + | args: &[il::Val], |
|
| 522 | + | dst: ?il::Reg |
|
| 523 | + | ) where 'scratch: 'selection { |
|
| 524 | + | // For indirect calls, save target to scratch register before arg |
|
| 525 | + | // setup can clobber it. |
|
| 526 | + | if let case il::Val::Reg(r) = *func { |
|
| 527 | + | let target = getSrcReg(s, r, super::SCRATCH2); |
|
| 528 | + | emitMv(s, super::SCRATCH2, target); |
|
| 529 | + | } |
|
| 530 | + | // Move arguments to A0-A7 using parallel move resolution. |
|
| 531 | + | if args.len > super::ARG_REGS.len { |
|
| 532 | + | set s.e.error = super::Error::Capacity; |
|
| 533 | + | return; |
|
| 534 | + | } |
|
| 535 | + | emitParallelMoves(s, &super::ARG_REGS[..], args); |
|
| 536 | + | ||
| 537 | + | // Emit call. |
|
| 538 | + | match *func { |
|
| 539 | + | case il::Val::FnAddr(name) => { |
|
| 540 | + | emit::recordCall(s.e, name); |
|
| 543 | 541 | }, |
|
| 544 | - | else => selectFixedInstr(s, blockIdx, instr, frame), |
|
| 542 | + | case il::Val::Reg(_) => { |
|
| 543 | + | emit::emit(s.e, encode::jalr(super::RA, super::SCRATCH2, 0)); |
|
| 544 | + | }, |
|
| 545 | + | else => { |
|
| 546 | + | panic "selectCall: invalid call target"; |
|
| 547 | + | } |
|
| 548 | + | } |
|
| 549 | + | // Move result from A0. |
|
| 550 | + | if let d = dst { |
|
| 551 | + | let rd = getDstReg(s, d, super::SCRATCH1); |
|
| 552 | + | emitMv(s, rd, super::A0); |
|
| 545 | 553 | } |
|
| 546 | 554 | } |
|
| 547 | 555 | ||
| 548 | 556 | /// Select RV64 instructions for inline IL operands. |
|
| 549 | 557 | fn selectFixedInstr 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, instr: &il::Instr, frame: &emit::Frame) where 'scratch: 'selection { |
test/tests/call.checked.selection.rad
added
+30 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | //! Full-arity calls preserve argument order and live values. |
|
| 3 | + | ||
| 4 | + | /// Encode eight arguments in distinct decimal positions. |
|
| 5 | + | fn digits(a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64) -> u64 { |
|
| 6 | + | return a + 10 * b + 100 * c + 1000 * d + 10000 * e + 100000 * f + 1000000 * g + 10000000 * h; |
|
| 7 | + | } |
|
| 8 | + | ||
| 9 | + | /// Retain argument values and the target across two indirect calls. |
|
| 10 | + | fn invoke(callback: fn(u64, u64, u64, u64, u64, u64, u64, u64) -> u64, values: &[u64]) -> u64 { |
|
| 11 | + | let a = values[0]; |
|
| 12 | + | let b = values[1]; |
|
| 13 | + | let c = values[2]; |
|
| 14 | + | let d = values[3]; |
|
| 15 | + | let e = values[4]; |
|
| 16 | + | let f = values[5]; |
|
| 17 | + | let g = values[6]; |
|
| 18 | + | let h = values[7]; |
|
| 19 | + | let reversed = callback(h, g, f, e, d, c, b, a); |
|
| 20 | + | let ordered = callback(a, b, c, d, e, f, g, h); |
|
| 21 | + | return reversed + ordered + a + b + c + d + e + f + g + h; |
|
| 22 | + | } |
|
| 23 | + | ||
| 24 | + | /// Check direct argument placement and repeated indirect calls. |
|
| 25 | + | @default fn main() -> u32 { |
|
| 26 | + | let values: [u64; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; |
|
| 27 | + | assert digits(1, 2, 3, 4, 5, 6, 7, 8) == 87654321; |
|
| 28 | + | assert invoke(digits, &values[..]) == 100000035; |
|
| 29 | + | return 0; |
|
| 30 | + | } |