Expose memory ordering to safe code
673ae4f6c906ace05e15a21ff85400cab3f15027891a7a17e03f90e76b025423
1 parent
3797da49
lib/std/arch/rv64/isel.rad
+3 -0
| 745 | 745 | emitMv(s, ecallRd, super::A0); |
|
| 746 | 746 | }, |
|
| 747 | 747 | case il::Instr::Ebreak => { |
|
| 748 | 748 | emit::emit(s.e, encode::ebreak()); |
|
| 749 | 749 | }, |
|
| 750 | + | case il::Instr::MemoryFence => { |
|
| 751 | + | emit::emit(s.e, encode::fence()); |
|
| 752 | + | }, |
|
| 750 | 753 | } |
|
| 751 | 754 | } |
|
| 752 | 755 | ||
| 753 | 756 | /// Choose the cheapest canonical representation that preserves the comparison. |
|
| 754 | 757 | /// RV64 word operations naturally sign-extend, and sign-extension preserves |
lib/std/intrinsics.rad
+3 -0
| 7 | 7 | /// emulator addresses and 64-bit native (AMD64) pointers. |
|
| 8 | 8 | @intrinsic export fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 9 | 9 | ||
| 10 | 10 | /// Break out of program. |
|
| 11 | 11 | @intrinsic export fn ebreak(); |
|
| 12 | + | ||
| 13 | + | /// Full acquire/release memory fence. |
|
| 14 | + | @intrinsic export fn memoryFence(); |
lib/std/lang/il.rad
+4 -1
| 293 | 293 | /// Environment call: `ecall %dst <num> <a0> <a1> <a2> <a3>;` |
|
| 294 | 294 | Ecall { dst: Reg, num: Val, a0: Val, a1: Val, a2: Val, a3: Val }, |
|
| 295 | 295 | /// Environment break: `ebreak;`. |
|
| 296 | 296 | /// Triggers a breakpoint exception for debugging. |
|
| 297 | 297 | Ebreak, |
|
| 298 | + | /// Full acquire/release memory fence. |
|
| 299 | + | MemoryFence, |
|
| 298 | 300 | } |
|
| 299 | 301 | ||
| 300 | 302 | ////////////////////////// |
|
| 301 | 303 | // Blocks and Functions // |
|
| 302 | 304 | ////////////////////////// |
| 495 | 497 | withReg(a1, f, ctx); |
|
| 496 | 498 | withReg(a2, f, ctx); |
|
| 497 | 499 | withReg(a3, f, ctx); |
|
| 498 | 500 | }, |
|
| 499 | 501 | case Instr::Unreachable, |
|
| 500 | - | Instr::Ebreak => {}, |
|
| 502 | + | Instr::Ebreak, |
|
| 503 | + | Instr::MemoryFence => {}, |
|
| 501 | 504 | } |
|
| 502 | 505 | } |
|
| 503 | 506 | ||
| 504 | 507 | /// Call callback if value is a register. |
|
| 505 | 508 | fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
lib/std/lang/il/printer.rad
+3 -0
| 358 | 358 | writeVal(out, a, a3); |
|
| 359 | 359 | } |
|
| 360 | 360 | case super::Instr::Ebreak => { |
|
| 361 | 361 | write(out, "ebreak"); |
|
| 362 | 362 | } |
|
| 363 | + | case super::Instr::MemoryFence => { |
|
| 364 | + | write(out, "memory-fence"); |
|
| 365 | + | } |
|
| 363 | 366 | } |
|
| 364 | 367 | } |
|
| 365 | 368 | ||
| 366 | 369 | /// Write a typed binary operation: `op type %dst %a %b`. |
|
| 367 | 370 | fn writeTypedBinOp( |
lib/std/lang/lower.rad
+11 -0
| 6860 | 6860 | // Check for known intrinsic names. |
|
| 6861 | 6861 | if mem::eq(sym.name, "ecall") { |
|
| 6862 | 6862 | return try lowerEcall(self, call); |
|
| 6863 | 6863 | } else if mem::eq(sym.name, "ebreak") { |
|
| 6864 | 6864 | return try lowerEbreak(self, call); |
|
| 6865 | + | } else if mem::eq(sym.name, "memoryFence") { |
|
| 6866 | + | return try lowerMemoryFence(self, call); |
|
| 6865 | 6867 | } else { |
|
| 6866 | 6868 | throw LowerError::UnknownIntrinsic; |
|
| 6867 | 6869 | } |
|
| 6868 | 6870 | } |
|
| 6869 | 6871 |
| 6892 | 6894 | emit(self, il::Instr::Ebreak); |
|
| 6893 | 6895 | ||
| 6894 | 6896 | return il::Val::Undef; |
|
| 6895 | 6897 | } |
|
| 6896 | 6898 | ||
| 6899 | + | /// Lower `memoryFence()`. |
|
| 6900 | + | fn lowerMemoryFence(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6901 | + | if call.args.len <> 0 { |
|
| 6902 | + | throw LowerError::InvalidArgCount; |
|
| 6903 | + | } |
|
| 6904 | + | emit(self, il::Instr::MemoryFence); |
|
| 6905 | + | return il::Val::Undef; |
|
| 6906 | + | } |
|
| 6907 | + | ||
| 6897 | 6908 | /// Resolve callee to an IL value. For direct function calls, use the symbol name. |
|
| 6898 | 6909 | /// For variables holding function pointers or complex expressions (eg. `array[i]()`), |
|
| 6899 | 6910 | /// lower the callee expression. |
|
| 6900 | 6911 | fn lowerCallee(self: *mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) { |
|
| 6901 | 6912 | if let sym = resolver::nodeData(self.low.resolver, callee).sym { |
test/tests/builtin.memory.fence.rad
added
+7 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | @intrinsic fn memoryFence(); |
|
| 3 | + | ||
| 4 | + | @default fn main() -> i32 { |
|
| 5 | + | memoryFence(); |
|
| 6 | + | return 0; |
|
| 7 | + | } |