Add new `set` keyword
4ef0a4c342dfed4f34598dbacf345e91b72955ee1cafa0270c23db912199eee4
1 parent
05324b73
lib/std/lang/ast.rad
+15 -0
| 813 | 813 | span: Span, |
|
| 814 | 814 | /// Variant-specific payload for the node. |
|
| 815 | 815 | value: NodeValue, |
|
| 816 | 816 | } |
|
| 817 | 817 | ||
| 818 | + | /// Check whether a node is a place expression. |
|
| 819 | + | /// |
|
| 820 | + | /// Place expressions have persistent storage and can appear on the left side |
|
| 821 | + | /// of `set` assignments. |
|
| 822 | + | export fn isPlaceExpr(node: *Node) -> bool { |
|
| 823 | + | match node.value { |
|
| 824 | + | case NodeValue::Ident(_), |
|
| 825 | + | NodeValue::ScopeAccess(_), |
|
| 826 | + | NodeValue::FieldAccess(_), |
|
| 827 | + | NodeValue::Subscript { .. }, |
|
| 828 | + | NodeValue::Deref(_) => return true, |
|
| 829 | + | else => return false, |
|
| 830 | + | } |
|
| 831 | + | } |
|
| 832 | + | ||
| 818 | 833 | /// Allocate a new AST node from the arena with the given span and value. |
|
| 819 | 834 | export fn allocNode(arena: *mut NodeArena, span: Span, value: NodeValue) -> *mut Node { |
|
| 820 | 835 | let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node)); |
|
| 821 | 836 | let node = p as *mut Node; |
|
| 822 | 837 | let nodeId = arena.nextId; |
lib/std/lang/gen/bitset.rad
+1 -1
| 55 | 55 | ||
| 56 | 56 | return init(bits); |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | 59 | /// Set bit `n` in the bitset. |
|
| 60 | - | export fn set(bs: *mut Bitset, n: u32) { |
|
| 60 | + | export fn put(bs: *mut Bitset, n: u32) { |
|
| 61 | 61 | if n >= bs.len { |
|
| 62 | 62 | return; |
|
| 63 | 63 | } |
|
| 64 | 64 | let word = n / 32; |
|
| 65 | 65 | let b = n % 32; |
lib/std/lang/gen/bitset/tests.rad
+37 -37
| 12 | 12 | try testing::expect(not super::contains(&bs, 31)); |
|
| 13 | 13 | try testing::expect(not super::contains(&bs, 32)); |
|
| 14 | 14 | try testing::expect(not super::contains(&bs, 127)); |
|
| 15 | 15 | ||
| 16 | 16 | // Set various bits including word boundaries. |
|
| 17 | - | super::set(&mut bs, 0); |
|
| 18 | - | super::set(&mut bs, 31); |
|
| 19 | - | super::set(&mut bs, 32); |
|
| 20 | - | super::set(&mut bs, 127); |
|
| 17 | + | super::put(&mut bs, 0); |
|
| 18 | + | super::put(&mut bs, 31); |
|
| 19 | + | super::put(&mut bs, 32); |
|
| 20 | + | super::put(&mut bs, 127); |
|
| 21 | 21 | ||
| 22 | 22 | try testing::expect(super::contains(&bs, 0)); |
|
| 23 | 23 | try testing::expect(super::contains(&bs, 31)); |
|
| 24 | 24 | try testing::expect(super::contains(&bs, 32)); |
|
| 25 | 25 | try testing::expect(super::contains(&bs, 127)); |
| 34 | 34 | /// Test clear operation. |
|
| 35 | 35 | @test fn testClear() throws (testing::TestError) { |
|
| 36 | 36 | let mut bits: [u32; 2] = [0; 2]; |
|
| 37 | 37 | let mut bs = super::new(&mut bits[..]); |
|
| 38 | 38 | ||
| 39 | - | super::set(&mut bs, 0); |
|
| 40 | - | super::set(&mut bs, 31); |
|
| 41 | - | super::set(&mut bs, 32); |
|
| 39 | + | super::put(&mut bs, 0); |
|
| 40 | + | super::put(&mut bs, 31); |
|
| 41 | + | super::put(&mut bs, 32); |
|
| 42 | 42 | ||
| 43 | 43 | try testing::expect(super::contains(&bs, 0)); |
|
| 44 | 44 | try testing::expect(super::contains(&bs, 31)); |
|
| 45 | 45 | try testing::expect(super::contains(&bs, 32)); |
|
| 46 | 46 |
| 56 | 56 | let mut bits: [u32; 2] = [0; 2]; |
|
| 57 | 57 | let mut bs = super::new(&mut bits[..]); |
|
| 58 | 58 | ||
| 59 | 59 | try testing::expect(super::count(&bs) == 0); |
|
| 60 | 60 | ||
| 61 | - | super::set(&mut bs, 0); |
|
| 61 | + | super::put(&mut bs, 0); |
|
| 62 | 62 | try testing::expect(super::count(&bs) == 1); |
|
| 63 | 63 | ||
| 64 | - | super::set(&mut bs, 31); |
|
| 65 | - | super::set(&mut bs, 32); |
|
| 66 | - | super::set(&mut bs, 63); |
|
| 64 | + | super::put(&mut bs, 31); |
|
| 65 | + | super::put(&mut bs, 32); |
|
| 66 | + | super::put(&mut bs, 63); |
|
| 67 | 67 | try testing::expect(super::count(&bs) == 4); |
|
| 68 | 68 | ||
| 69 | 69 | super::clear(&mut bs, 31); |
|
| 70 | 70 | try testing::expect(super::count(&bs) == 3); |
|
| 71 | 71 | } |
| 75 | 75 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 76 | 76 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 77 | 77 | let mut a = super::new(&mut bits_a[..]); |
|
| 78 | 78 | let mut b = super::new(&mut bits_b[..]); |
|
| 79 | 79 | ||
| 80 | - | super::set(&mut a, 0); |
|
| 81 | - | super::set(&mut a, 10); |
|
| 80 | + | super::put(&mut a, 0); |
|
| 81 | + | super::put(&mut a, 10); |
|
| 82 | 82 | ||
| 83 | - | super::set(&mut b, 10); |
|
| 84 | - | super::set(&mut b, 20); |
|
| 83 | + | super::put(&mut b, 10); |
|
| 84 | + | super::put(&mut b, 20); |
|
| 85 | 85 | ||
| 86 | 86 | super::union_(&mut a, &b); |
|
| 87 | 87 | ||
| 88 | 88 | try testing::expect(super::contains(&a, 0)); |
|
| 89 | 89 | try testing::expect(super::contains(&a, 10)); |
| 96 | 96 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 97 | 97 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 98 | 98 | let mut a = super::new(&mut bits_a[..]); |
|
| 99 | 99 | let mut b = super::new(&mut bits_b[..]); |
|
| 100 | 100 | ||
| 101 | - | super::set(&mut a, 0); |
|
| 102 | - | super::set(&mut a, 10); |
|
| 103 | - | super::set(&mut a, 20); |
|
| 101 | + | super::put(&mut a, 0); |
|
| 102 | + | super::put(&mut a, 10); |
|
| 103 | + | super::put(&mut a, 20); |
|
| 104 | 104 | ||
| 105 | - | super::set(&mut b, 10); |
|
| 106 | - | super::set(&mut b, 30); |
|
| 105 | + | super::put(&mut b, 10); |
|
| 106 | + | super::put(&mut b, 30); |
|
| 107 | 107 | ||
| 108 | 108 | super::subtract(&mut a, &b); |
|
| 109 | 109 | ||
| 110 | 110 | try testing::expect(super::contains(&a, 0)); |
|
| 111 | 111 | try testing::expect(not super::contains(&a, 10)); |
| 121 | 121 | let mut b = super::new(&mut bits_b[..]); |
|
| 122 | 122 | ||
| 123 | 123 | // Both empty. |
|
| 124 | 124 | try testing::expect(super::eq(&a, &b)); |
|
| 125 | 125 | ||
| 126 | - | super::set(&mut a, 5); |
|
| 126 | + | super::put(&mut a, 5); |
|
| 127 | 127 | try testing::expect(not super::eq(&a, &b)); |
|
| 128 | 128 | ||
| 129 | - | super::set(&mut b, 5); |
|
| 129 | + | super::put(&mut b, 5); |
|
| 130 | 130 | try testing::expect(super::eq(&a, &b)); |
|
| 131 | 131 | ||
| 132 | - | super::set(&mut a, 32); |
|
| 133 | - | super::set(&mut b, 32); |
|
| 132 | + | super::put(&mut a, 32); |
|
| 133 | + | super::put(&mut b, 32); |
|
| 134 | 134 | try testing::expect(super::eq(&a, &b)); |
|
| 135 | 135 | ||
| 136 | - | super::set(&mut b, 33); |
|
| 136 | + | super::put(&mut b, 33); |
|
| 137 | 137 | try testing::expect(not super::eq(&a, &b)); |
|
| 138 | 138 | } |
|
| 139 | 139 | ||
| 140 | 140 | /// Test copy operation. |
|
| 141 | 141 | @test fn testCopy() throws (testing::TestError) { |
|
| 142 | 142 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 143 | 143 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 144 | 144 | let mut a = super::new(&mut bits_a[..]); |
|
| 145 | 145 | let mut b = super::new(&mut bits_b[..]); |
|
| 146 | 146 | ||
| 147 | - | super::set(&mut a, 0); |
|
| 148 | - | super::set(&mut a, 31); |
|
| 149 | - | super::set(&mut a, 63); |
|
| 147 | + | super::put(&mut a, 0); |
|
| 148 | + | super::put(&mut a, 31); |
|
| 149 | + | super::put(&mut a, 63); |
|
| 150 | 150 | ||
| 151 | 151 | super::copy(&mut b, &a); |
|
| 152 | 152 | ||
| 153 | 153 | try testing::expect(super::eq(&a, &b)); |
|
| 154 | 154 | try testing::expect(super::contains(&b, 0)); |
| 159 | 159 | /// Test clearAll operation. |
|
| 160 | 160 | @test fn testClearAll() throws (testing::TestError) { |
|
| 161 | 161 | let mut bits: [u32; 2] = [0; 2]; |
|
| 162 | 162 | let mut bs = super::new(&mut bits[..]); |
|
| 163 | 163 | ||
| 164 | - | super::set(&mut bs, 0); |
|
| 165 | - | super::set(&mut bs, 31); |
|
| 166 | - | super::set(&mut bs, 32); |
|
| 167 | - | super::set(&mut bs, 63); |
|
| 164 | + | super::put(&mut bs, 0); |
|
| 165 | + | super::put(&mut bs, 31); |
|
| 166 | + | super::put(&mut bs, 32); |
|
| 167 | + | super::put(&mut bs, 63); |
|
| 168 | 168 | try testing::expect(super::count(&bs) == 4); |
|
| 169 | 169 | ||
| 170 | 170 | super::clearAll(&mut bs); |
|
| 171 | 171 | try testing::expect(super::count(&bs) == 0); |
|
| 172 | 172 | try testing::expect(not super::contains(&bs, 0)); |
| 176 | 176 | /// Test iteration over set bits. |
|
| 177 | 177 | @test fn testIter() throws (testing::TestError) { |
|
| 178 | 178 | let mut bits: [u32; 2] = [0; 2]; |
|
| 179 | 179 | let mut bs = super::new(&mut bits[..]); |
|
| 180 | 180 | ||
| 181 | - | super::set(&mut bs, 3); |
|
| 182 | - | super::set(&mut bs, 31); |
|
| 183 | - | super::set(&mut bs, 32); |
|
| 184 | - | super::set(&mut bs, 50); |
|
| 181 | + | super::put(&mut bs, 3); |
|
| 182 | + | super::put(&mut bs, 31); |
|
| 183 | + | super::put(&mut bs, 32); |
|
| 184 | + | super::put(&mut bs, 50); |
|
| 185 | 185 | ||
| 186 | 186 | let mut it = super::iter(&bs); |
|
| 187 | 187 | let mut count: u32 = 0; |
|
| 188 | 188 | let mut sum: u32 = 0; |
|
| 189 | 189 |
| 221 | 221 | @test fn testOutOfBounds() throws (testing::TestError) { |
|
| 222 | 222 | let mut bits: [u32; 1] = [0; 1]; |
|
| 223 | 223 | let mut bs = super::new(&mut bits[..]); |
|
| 224 | 224 | ||
| 225 | 225 | // Setting beyond length should be ignored. |
|
| 226 | - | super::set(&mut bs, 100); |
|
| 226 | + | super::put(&mut bs, 100); |
|
| 227 | 227 | try testing::expect(not super::contains(&bs, 100)); |
|
| 228 | 228 | ||
| 229 | 229 | // Clearing beyond length should be safe. |
|
| 230 | 230 | super::clear(&mut bs, 100); |
|
| 231 | 231 |
lib/std/lang/gen/regalloc/assign.rad
+3 -3
| 110 | 110 | for k in 0..pred.n { |
|
| 111 | 111 | if bitset::contains(&live.liveIn[b], pred.virtRegs[k]) { |
|
| 112 | 112 | current.virtRegs[current.n] = pred.virtRegs[k]; |
|
| 113 | 113 | current.physRegs[current.n] = pred.physRegs[k]; |
|
| 114 | 114 | current.n += 1; |
|
| 115 | - | bitset::set(&mut usedRegs, *pred.physRegs[k] as u32); |
|
| 115 | + | bitset::put(&mut usedRegs, *pred.physRegs[k] as u32); |
|
| 116 | 116 | } |
|
| 117 | 117 | } |
|
| 118 | 118 | } |
|
| 119 | 119 | ||
| 120 | 120 | // Mark all live-in values' registers as used. |
| 126 | 126 | let mut liveInIter = bitset::iter(&live.liveIn[b]); |
|
| 127 | 127 | while let ssaReg = bitset::iterNext(&mut liveInIter) { |
|
| 128 | 128 | let reg = il::Reg { n: ssaReg }; |
|
| 129 | 129 | if not spill::isSpilled(spillInfo, reg) { |
|
| 130 | 130 | if let phys = assignments[ssaReg] { |
|
| 131 | - | bitset::set(&mut usedRegs, *phys as u32); |
|
| 131 | + | bitset::put(&mut usedRegs, *phys as u32); |
|
| 132 | 132 | // Also track in current mapping for block-end state. |
|
| 133 | 133 | if rmapFind(¤t, ssaReg) == nil { |
|
| 134 | 134 | rmapSet(&mut current, ssaReg, phys); |
|
| 135 | 135 | } |
|
| 136 | 136 | } else { |
| 239 | 239 | /// Find first free register in pool, allocate it, return it. |
|
| 240 | 240 | fn findFreeInPool(usedRegs: *mut bitset::Bitset, current: *mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg { |
|
| 241 | 241 | for i in 0..pool.len { |
|
| 242 | 242 | let r = pool[i]; |
|
| 243 | 243 | if not bitset::contains(usedRegs, *r as u32) { |
|
| 244 | - | bitset::set(usedRegs, *r as u32); |
|
| 244 | + | bitset::put(usedRegs, *r as u32); |
|
| 245 | 245 | rmapSet(current, ssaReg, r); |
|
| 246 | 246 | return r; |
|
| 247 | 247 | } |
|
| 248 | 248 | } |
|
| 249 | 249 | return nil; |
lib/std/lang/gen/regalloc/liveness.rad
+3 -3
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | 160 | /// Compute local defs and uses for a single block. |
|
| 161 | 161 | fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) { |
|
| 162 | 162 | for p in block.params { |
|
| 163 | - | bitset::set(defs, p.value.n); |
|
| 163 | + | bitset::put(defs, p.value.n); |
|
| 164 | 164 | } |
|
| 165 | 165 | for i in 0..block.instrs.len { |
|
| 166 | 166 | let instr = block.instrs[i]; |
|
| 167 | 167 | let mut ctx = DefsUses { defs, uses }; |
|
| 168 | 168 | il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque); |
|
| 169 | 169 | ||
| 170 | 170 | if let dst = il::instrDst(instr) { |
|
| 171 | - | bitset::set(defs, dst.n); |
|
| 171 | + | bitset::put(defs, dst.n); |
|
| 172 | 172 | } |
|
| 173 | 173 | } |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Callback for [`il::forEachReg`]: adds register to uses if not already defined. |
|
| 177 | 177 | fn addUseCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 178 | 178 | let c = ctx as *mut DefsUses; |
|
| 179 | 179 | if not bitset::contains(c.defs, reg.n) { |
|
| 180 | - | bitset::set(c.uses, reg.n); |
|
| 180 | + | bitset::put(c.uses, reg.n); |
|
| 181 | 181 | } |
|
| 182 | 182 | } |
|
| 183 | 183 | ||
| 184 | 184 | /// Callback for [`il::forEachReg`]: updates max register number. |
|
| 185 | 185 | fn maxRegCallback(reg: il::Reg, ctx: *mut opaque) { |
lib/std/lang/gen/regalloc/spill.rad
+6 -6
| 155 | 155 | // a callee-saved register for cross-call values. |
|
| 156 | 156 | while bitset::count(&calleeClass) > numCalleeSaved { |
|
| 157 | 157 | let mut it = bitset::iter(&calleeClass); |
|
| 158 | 158 | if let reg = bitset::iterNext(&mut it) { |
|
| 159 | 159 | bitset::clear(&mut calleeClass, reg); |
|
| 160 | - | bitset::set(&mut spilled, reg); |
|
| 160 | + | bitset::put(&mut spilled, reg); |
|
| 161 | 161 | } else { |
|
| 162 | 162 | panic "spill: count > 0 but no set bits found"; |
|
| 163 | 163 | } |
|
| 164 | 164 | } |
|
| 165 | 165 |
| 224 | 224 | } |
|
| 225 | 225 | c.entries[j] = key; |
|
| 226 | 226 | } |
|
| 227 | 227 | let toSpill = c.n if excess > c.n else excess; |
|
| 228 | 228 | for i in 0..toSpill { |
|
| 229 | - | bitset::set(spilled, c.entries[i].reg); |
|
| 229 | + | bitset::put(spilled, c.entries[i].reg); |
|
| 230 | 230 | bitset::clear(source, c.entries[i].reg); |
|
| 231 | 231 | } |
|
| 232 | 232 | } |
|
| 233 | 233 | ||
| 234 | 234 | /// Collect all values from a bitset into a candidates buffer with their costs. |
|
| 235 | - | fn collectCandidates(set: *bitset::Bitset, costs: *[SpillCost]) -> Candidates { |
|
| 235 | + | fn collectCandidates(bs: *bitset::Bitset, costs: *[SpillCost]) -> Candidates { |
|
| 236 | 236 | let mut c = Candidates { entries: undefined, n: 0 }; |
|
| 237 | - | let mut it = bitset::iter(set); |
|
| 237 | + | let mut it = bitset::iter(bs); |
|
| 238 | 238 | while let reg = bitset::iterNext(&mut it) { |
|
| 239 | 239 | assert c.n < MAX_CANDIDATES, "collectCandidates: too many live values"; |
|
| 240 | 240 | if reg < costs.len { |
|
| 241 | 241 | c.entries[c.n] = CostEntry { reg, cost: costs[reg].defs + costs[reg].uses }; |
|
| 242 | 242 | c.n += 1; |
| 301 | 301 | } |
|
| 302 | 302 | // Mark crossing values that remain after spilling as callee-saved class. |
|
| 303 | 303 | let mut it2 = bitset::iter(live); |
|
| 304 | 304 | while let n = bitset::iterNext(&mut it2) { |
|
| 305 | 305 | if not isCallDst(callDst, n) { |
|
| 306 | - | bitset::set(calleeClass, n); |
|
| 306 | + | bitset::put(calleeClass, n); |
|
| 307 | 307 | } |
|
| 308 | 308 | } |
|
| 309 | 309 | } |
|
| 310 | 310 | ||
| 311 | 311 | /// Callback for [`il::forEachReg`]: increments use count for register. |
| 315 | 315 | ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight; |
|
| 316 | 316 | } |
|
| 317 | 317 | ||
| 318 | 318 | /// Callback for [`il::forEachReg`]: adds register to live set. |
|
| 319 | 319 | fn addRegToSetCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 320 | - | bitset::set(ctx as *mut bitset::Bitset, reg.n); |
|
| 320 | + | bitset::put(ctx as *mut bitset::Bitset, reg.n); |
|
| 321 | 321 | } |
|
| 322 | 322 | ||
| 323 | 323 | /// Check if a register is spilled. |
|
| 324 | 324 | export fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool { |
|
| 325 | 325 | if reg.n >= info.maxReg { |
lib/std/lang/lower.rad
+22 -22
| 6708 | 6708 | emit(self, il::Instr::Zext { typ: extType, dst, val }); |
|
| 6709 | 6709 | } |
|
| 6710 | 6710 | return il::Val::Reg(dst); |
|
| 6711 | 6711 | } |
|
| 6712 | 6712 | ||
| 6713 | + | /// Lower a global value symbol. |
|
| 6714 | + | fn lowerGlobalValue(self: *mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val { |
|
| 6715 | + | // Function pointer reference: return the function's address directly. |
|
| 6716 | + | // Functions have no separate storage cell in the data section. |
|
| 6717 | + | if let case resolver::Type::Fn(_) = ty { |
|
| 6718 | + | return il::Val::Reg(emitFnAddr(self, sym)); |
|
| 6719 | + | } |
|
| 6720 | + | let src = emitDataAddr(self, sym); |
|
| 6721 | + | ||
| 6722 | + | return emitRead(self, src, 0, ty); |
|
| 6723 | + | } |
|
| 6724 | + | ||
| 6713 | 6725 | /// Lower an identifier that refers to a global symbol. |
|
| 6714 | 6726 | fn lowerGlobalSymbol(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 6715 | 6727 | // First try to get a compile-time constant value. |
|
| 6716 | 6728 | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 6717 | 6729 | return try constValueToVal(self, constVal, node); |
|
| 6718 | 6730 | } |
|
| 6719 | 6731 | // Otherwise get the symbol. |
|
| 6720 | 6732 | let sym = try symOf(self, node); |
|
| 6721 | - | let mut ty: resolver::Type = undefined; |
|
| 6722 | 6733 | ||
| 6723 | 6734 | match sym.data { |
|
| 6724 | - | case resolver::SymbolData::Constant { type, .. } => |
|
| 6725 | - | ty = type, |
|
| 6726 | - | case resolver::SymbolData::Value { type, .. } => { |
|
| 6727 | - | // Function pointer reference: just return the address, don't load. |
|
| 6728 | - | // Functions don't have a separate storage location holding their |
|
| 6729 | - | // address; they just exist at an address in the code section. |
|
| 6730 | - | if let case resolver::Type::Fn(_) = type { |
|
| 6731 | - | return il::Val::Reg(emitFnAddr(self, sym)); |
|
| 6732 | - | } |
|
| 6733 | - | ty = type; |
|
| 6735 | + | case resolver::SymbolData::Constant { type, .. } => { |
|
| 6736 | + | let src = emitDataAddr(self, sym); |
|
| 6737 | + | ||
| 6738 | + | return emitRead(self, src, 0, type); |
|
| 6734 | 6739 | } |
|
| 6740 | + | case resolver::SymbolData::Value { type, .. } => |
|
| 6741 | + | return lowerGlobalValue(self, sym, type), |
|
| 6735 | 6742 | else => throw LowerError::UnexpectedNodeValue(node), |
|
| 6736 | 6743 | } |
|
| 6737 | - | let dst = emitDataAddr(self, sym); |
|
| 6738 | - | ||
| 6739 | - | return emitRead(self, dst, 0, ty); |
|
| 6740 | 6744 | } |
|
| 6741 | 6745 | ||
| 6742 | 6746 | /// Lower an assignment to a static variable. |
|
| 6743 | 6747 | fn lowerStaticAssign(self: *mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) { |
|
| 6744 | 6748 | let sym = try symOf(self, target); |
| 6794 | 6798 | ||
| 6795 | 6799 | return il::Val::Reg(dst); |
|
| 6796 | 6800 | } |
|
| 6797 | 6801 | return emitRead(self, src, 0, type); |
|
| 6798 | 6802 | } |
|
| 6799 | - | case resolver::SymbolData::Value { type, .. } => { |
|
| 6800 | - | // Function pointer reference. |
|
| 6801 | - | if let case resolver::Type::Fn(_) = type { |
|
| 6802 | - | return il::Val::Reg(emitFnAddr(self, sym)); |
|
| 6803 | - | } |
|
| 6804 | - | throw LowerError::ExpectedFunction; |
|
| 6805 | - | } |
|
| 6806 | - | else => throw LowerError::UnexpectedNodeValue(node), |
|
| 6803 | + | case resolver::SymbolData::Value { type, .. } => |
|
| 6804 | + | return lowerGlobalValue(self, sym, type), |
|
| 6805 | + | else => |
|
| 6806 | + | throw LowerError::UnexpectedNodeValue(node), |
|
| 6807 | 6807 | } |
|
| 6808 | 6808 | } |
|
| 6809 | 6809 | ||
| 6810 | 6810 | /// Lower an expression AST node to an IL value. |
|
| 6811 | 6811 | /// This is the main expression dispatch, all expression nodes go through here. |
lib/std/lang/parser.rad
+10 -0
| 620 | 620 | match node.value { |
|
| 621 | 621 | case ast::NodeValue::Ident(_) => |
|
| 622 | 622 | return true, |
|
| 623 | 623 | case ast::NodeValue::FieldAccess(_) => |
|
| 624 | 624 | return true, |
|
| 625 | + | case ast::NodeValue::ScopeAccess(_) => |
|
| 626 | + | return true, |
|
| 625 | 627 | case ast::NodeValue::Subscript { .. } => |
|
| 626 | 628 | return true, |
|
| 627 | 629 | case ast::NodeValue::Deref(_) => |
|
| 628 | 630 | return true, |
|
| 629 | 631 | else => |
| 984 | 986 | if consume(p, scanner::TokenKind::Mut) { |
|
| 985 | 987 | return try parseLet(p, true); |
|
| 986 | 988 | } |
|
| 987 | 989 | return try parseLet(p, false); |
|
| 988 | 990 | } |
|
| 991 | + | case scanner::TokenKind::Set => { |
|
| 992 | + | advance(p); |
|
| 993 | + | let stmt = try parseExprStmt(p); |
|
| 994 | + | if let case ast::NodeValue::Assign(_) = stmt.value { |
|
| 995 | + | return stmt; |
|
| 996 | + | } |
|
| 997 | + | throw failParsing(p, "expected assignment after `set`"); |
|
| 998 | + | } |
|
| 989 | 999 | case scanner::TokenKind::Constant => { |
|
| 990 | 1000 | return try parseConst(p, attrs); |
|
| 991 | 1001 | } |
|
| 992 | 1002 | case scanner::TokenKind::Static => { |
|
| 993 | 1003 | return try parseStatic(p, attrs); |
lib/std/lang/parser/tests.rad
+9 -0
| 2220 | 2220 | else throw testing::TestError::Failed; |
|
| 2221 | 2221 | let case ast::NodeValue::BinOp(bin) = node.right.value |
|
| 2222 | 2222 | else throw testing::TestError::Failed; |
|
| 2223 | 2223 | try testing::expect(bin.op == ast::BinaryOp::Add); |
|
| 2224 | 2224 | } |
|
| 2225 | + | { |
|
| 2226 | + | let assign = try! parseStmtStr("set module::VAR = 1;"); |
|
| 2227 | + | let case ast::NodeValue::Assign(node) = assign.value |
|
| 2228 | + | else throw testing::TestError::Failed; |
|
| 2229 | + | let case ast::NodeValue::ScopeAccess(access) = node.left.value |
|
| 2230 | + | else throw testing::TestError::Failed; |
|
| 2231 | + | try expectIdent(access.parent, "module"); |
|
| 2232 | + | try expectIdent(access.child, "VAR"); |
|
| 2233 | + | } |
|
| 2225 | 2234 | } |
|
| 2226 | 2235 | ||
| 2227 | 2236 | /// Test parsing basic arithmetic binary operators (+, -, *, /, %). |
|
| 2228 | 2237 | @test fn testParseBinOpArithmetic() throws (testing::TestError) { |
|
| 2229 | 2238 | let add = try! parseExprStr("a + b"); |
lib/std/lang/resolver.rad
+24 -0
| 5694 | 5694 | } |
|
| 5695 | 5695 | case ast::NodeValue::FieldAccess(access) => { |
|
| 5696 | 5696 | let _ = try infer(self, access.parent); |
|
| 5697 | 5697 | return try canBorrowMutFrom(self, access.parent); |
|
| 5698 | 5698 | } |
|
| 5699 | + | case ast::NodeValue::ScopeAccess(_) => { |
|
| 5700 | + | // Module-qualified access to a top-level symbol. A `static` |
|
| 5701 | + | // binds as a mutable value; a `constant` does not. |
|
| 5702 | + | let _ = try infer(self, node); |
|
| 5703 | + | let sym = nodeData(self, node).sym |
|
| 5704 | + | else return false; |
|
| 5705 | + | ||
| 5706 | + | if let case SymbolData::Value { mutable, .. } = sym.data { |
|
| 5707 | + | return mutable; |
|
| 5708 | + | } |
|
| 5709 | + | return false; |
|
| 5710 | + | } |
|
| 5699 | 5711 | case ast::NodeValue::Subscript { container, .. } => { |
|
| 5700 | 5712 | let containerTy = try infer(self, container); |
|
| 5701 | 5713 | // Subscript auto-derefs pointers, so check the actual indexed type. |
|
| 5702 | 5714 | let subjectTy = autoDeref(containerTy); |
|
| 5703 | 5715 |
| 5712 | 5724 | case ast::NodeValue::ArrayLit(_), |
|
| 5713 | 5725 | ast::NodeValue::ArrayRepeatLit(_) => |
|
| 5714 | 5726 | { |
|
| 5715 | 5727 | return true; |
|
| 5716 | 5728 | } |
|
| 5729 | + | case ast::NodeValue::Call(_) => { |
|
| 5730 | + | // A call returning `*mut T` (or `&mut [T]`) yields a |
|
| 5731 | + | // mutable place. Non-pointer returns cannot be mutably borrowed. |
|
| 5732 | + | let ty = try infer(self, node); |
|
| 5733 | + | if let case Type::Pointer { mutable, .. } = ty { |
|
| 5734 | + | return mutable; |
|
| 5735 | + | } |
|
| 5736 | + | if let case Type::Slice { mutable, .. } = ty { |
|
| 5737 | + | return mutable; |
|
| 5738 | + | } |
|
| 5739 | + | return false; |
|
| 5740 | + | } |
|
| 5717 | 5741 | case ast::NodeValue::Deref(inner) => { |
|
| 5718 | 5742 | let innerTy = try infer(self, inner); |
|
| 5719 | 5743 | ||
| 5720 | 5744 | if let case Type::Pointer { mutable, .. } = innerTy { |
|
| 5721 | 5745 | return mutable; |
lib/std/lang/resolver/tests.rad
+42 -0
| 3399 | 3399 | let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 3400 | 3400 | let result = try resolveProgramStr(&mut a, program); |
|
| 3401 | 3401 | try expectNoErrors(&result); |
|
| 3402 | 3402 | } |
|
| 3403 | 3403 | ||
| 3404 | + | /// Test borrowing mutably from a field access on a call returning `*mut`. |
|
| 3405 | + | @test fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) { |
|
| 3406 | + | let mut a = testResolver(); |
|
| 3407 | + | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
|
| 3408 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3409 | + | try expectNoErrors(&result); |
|
| 3410 | + | } |
|
| 3411 | + | ||
| 3412 | + | /// Test that calls returning immutable pointers cannot be mutably borrowed. |
|
| 3413 | + | @test fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) { |
|
| 3414 | + | let mut a = testResolver(); |
|
| 3415 | + | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { let b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
|
| 3416 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3417 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3418 | + | } |
|
| 3419 | + | ||
| 3420 | + | /// Test borrowing mutably from a public static through scope access. |
|
| 3421 | + | @test fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) { |
|
| 3422 | + | let mut a = testResolver(); |
|
| 3423 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3424 | + | ||
| 3425 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3426 | + | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena); |
|
| 3427 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; *p = 7; }", &mut arena); |
|
| 3428 | + | ||
| 3429 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3430 | + | try expectNoErrors(&result); |
|
| 3431 | + | } |
|
| 3432 | + | ||
| 3433 | + | /// Test that constants through scope access cannot be mutably borrowed. |
|
| 3434 | + | @test fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) { |
|
| 3435 | + | let mut a = testResolver(); |
|
| 3436 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3437 | + | ||
| 3438 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3439 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena); |
|
| 3440 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; *p = 9; }", &mut arena); |
|
| 3441 | + | ||
| 3442 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3443 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3444 | + | } |
|
| 3445 | + | ||
| 3404 | 3446 | /// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer. |
|
| 3405 | 3447 | @test fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) { |
|
| 3406 | 3448 | let mut a = testResolver(); |
|
| 3407 | 3449 | let program = "fn f() { let mut x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
|
| 3408 | 3450 | let result = try resolveProgramStr(&mut a, program); |
lib/std/lang/scanner.rad
+3 -2
| 89 | 89 | Continue, While, For, In, |
|
| 90 | 90 | Loop, Match, Case, Try, Catch, |
|
| 91 | 91 | Throw, Throws, Panic, Assert, |
|
| 92 | 92 | ||
| 93 | 93 | // Variable binding tokens. |
|
| 94 | - | Let, Mut, Constant, Align, |
|
| 94 | + | Let, Mut, Set, Constant, Align, |
|
| 95 | 95 | ||
| 96 | 96 | // Module-related tokens. |
|
| 97 | 97 | Mod, Use, Super, |
|
| 98 | 98 | ||
| 99 | 99 | // Type or function attributes. |
| 114 | 114 | /// Corresponding token. |
|
| 115 | 115 | tok: TokenKind, |
|
| 116 | 116 | } |
|
| 117 | 117 | ||
| 118 | 118 | /// Sorted keyword table for binary search. |
|
| 119 | - | constant KEYWORDS: [Keyword; 50] = [ |
|
| 119 | + | constant KEYWORDS: [Keyword; 51] = [ |
|
| 120 | 120 | { name: "align", tok: TokenKind::Align }, |
|
| 121 | 121 | { name: "and", tok: TokenKind::And }, |
|
| 122 | 122 | { name: "as", tok: TokenKind::As }, |
|
| 123 | 123 | { name: "assert", tok: TokenKind::Assert }, |
|
| 124 | 124 | { name: "bool", tok: TokenKind::Bool }, |
| 150 | 150 | { name: "opaque", tok: TokenKind::Opaque }, |
|
| 151 | 151 | { name: "or", tok: TokenKind::Or }, |
|
| 152 | 152 | { name: "panic", tok: TokenKind::Panic }, |
|
| 153 | 153 | { name: "record", tok: TokenKind::Record }, |
|
| 154 | 154 | { name: "return", tok: TokenKind::Return }, |
|
| 155 | + | { name: "set", tok: TokenKind::Set }, |
|
| 155 | 156 | { name: "static", tok: TokenKind::Static }, |
|
| 156 | 157 | { name: "super", tok: TokenKind::Super }, |
|
| 157 | 158 | { name: "throw", tok: TokenKind::Throw }, |
|
| 158 | 159 | { name: "throws", tok: TokenKind::Throws }, |
|
| 159 | 160 | { name: "trait", tok: TokenKind::Trait }, |
lib/std/tests.rad
+2 -2
| 315 | 315 | throw testing::TestError::Failed; |
|
| 316 | 316 | } |
|
| 317 | 317 | try testing::expect(vec::get(&v, 3) == nil); |
|
| 318 | 318 | ||
| 319 | 319 | let new: i32 = 999; |
|
| 320 | - | try testing::expect(vec::set(&mut v, 1, &new)); |
|
| 320 | + | try testing::expect(vec::put(&mut v, 1, &new)); |
|
| 321 | 321 | ||
| 322 | 322 | if let ptr = vec::get(&v, 1) { |
|
| 323 | 323 | let value: i32 = *(ptr as *i32); |
|
| 324 | 324 | try testing::expect(value == 999); |
|
| 325 | 325 | } else { |
|
| 326 | 326 | throw testing::TestError::Failed; |
|
| 327 | 327 | } |
|
| 328 | - | try testing::expectNot(vec::set(&mut v, 5, &new)); |
|
| 328 | + | try testing::expectNot(vec::put(&mut v, 5, &new)); |
|
| 329 | 329 | } |
|
| 330 | 330 | ||
| 331 | 331 | @test fn testVecCapacity() throws (testing::TestError) { |
|
| 332 | 332 | let mut arena: [u8; 16] align(4) = undefined; |
|
| 333 | 333 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
lib/std/vec.rad
+1 -1
| 97 | 97 | } |
|
| 98 | 98 | ||
| 99 | 99 | /// Set the element at the given index. |
|
| 100 | 100 | /// |
|
| 101 | 101 | /// Returns false if index is out of bounds. |
|
| 102 | - | export fn set(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 102 | + | export fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 103 | 103 | if index >= vec.len { |
|
| 104 | 104 | return false; |
|
| 105 | 105 | } |
|
| 106 | 106 | let off: u32 = index * vec.stride; |
|
| 107 | 107 | let dst: *mut u8 = &mut vec.data[off]; |
test/tests/mutref.loop.bug.rad
+5 -5
| 8 | 8 | //! |
|
| 9 | 9 | //! The critical case is when the loop executes zero iterations: the merge |
|
| 10 | 10 | //! block tries to `load` through the initial integer value (not a valid |
|
| 11 | 11 | //! pointer), crashing the program. |
|
| 12 | 12 | ||
| 13 | - | fn set(ptr: *mut u32, val: u32) { |
|
| 13 | + | fn store(ptr: *mut u32, val: u32) { |
|
| 14 | 14 | *ptr = val; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Zero-iteration loop with &mut inside the body. |
|
| 18 | 18 | /// Without the fix, `val` starts as integer 42 in SSA, but the post-loop |
|
| 19 | 19 | /// merge tries to `load` through it as if it were a pointer. |
|
| 20 | 20 | fn testZeroIter(n: u32) -> u32 { |
|
| 21 | 21 | let mut val: u32 = 42; |
|
| 22 | 22 | let mut i: u32 = 0; |
|
| 23 | 23 | while i < n { |
|
| 24 | - | set(&mut val, val + 1); |
|
| 24 | + | store(&mut val, val + 1); |
|
| 25 | 25 | i += 1; |
|
| 26 | 26 | } |
|
| 27 | 27 | return val; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Multiple iterations: accumulate via &mut pointer in a loop. |
|
| 31 | 31 | fn testMultiIter() -> u32 { |
|
| 32 | 32 | let mut acc: u32 = 0; |
|
| 33 | 33 | let mut i: u32 = 0; |
|
| 34 | 34 | while i < 5 { |
|
| 35 | - | set(&mut acc, acc + i); |
|
| 35 | + | store(&mut acc, acc + i); |
|
| 36 | 36 | i += 1; |
|
| 37 | 37 | } |
|
| 38 | 38 | return acc; |
|
| 39 | 39 | } |
|
| 40 | 40 |
| 42 | 42 | fn testMultipleVars() -> u32 { |
|
| 43 | 43 | let mut a: u32 = 0; |
|
| 44 | 44 | let mut b: u32 = 100; |
|
| 45 | 45 | let mut i: u32 = 0; |
|
| 46 | 46 | while i < 3 { |
|
| 47 | - | set(&mut a, a + 1); |
|
| 48 | - | set(&mut b, b - 1); |
|
| 47 | + | store(&mut a, a + 1); |
|
| 48 | + | store(&mut b, b - 1); |
|
| 49 | 49 | i += 1; |
|
| 50 | 50 | } |
|
| 51 | 51 | return a + b; |
|
| 52 | 52 | } |
|
| 53 | 53 |
test/tests/ref.if.bug.rad
+2 -2
| 2 | 2 | //! Regression test: address-of inside an if branch. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! When `&mut var` appears in only one branch of an if/else, the merge |
|
| 5 | 5 | //! block's phi merges the original integer value with a stack pointer. |
|
| 6 | 6 | ||
| 7 | - | fn set(ptr: *mut u32, val: u32) { |
|
| 7 | + | fn store(ptr: *mut u32, val: u32) { |
|
| 8 | 8 | *ptr = val; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn testIfBranch(cond: bool) -> u32 { |
|
| 12 | 12 | let mut val: u32 = 42; |
|
| 13 | 13 | if cond { |
|
| 14 | - | set(&mut val, 99); |
|
| 14 | + | store(&mut val, 99); |
|
| 15 | 15 | } |
|
| 16 | 16 | return val; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | @default fn main() -> i32 { |
test/tests/ref.mut.ptr.rad
+3 -3
| 1 | 1 | //! returns: 84 |
|
| 2 | 2 | //! Test mutable pointer references. |
|
| 3 | 3 | ||
| 4 | - | fn set(ptr: *mut i32) { |
|
| 4 | + | fn store(ptr: *mut i32) { |
|
| 5 | 5 | *ptr = 42; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | @default fn main() -> i32 { |
|
| 9 | 9 | let mut a: i32 = 0; |
|
| 10 | 10 | let p: *mut i32 = &mut a; |
|
| 11 | - | set(p); |
|
| 11 | + | store(p); |
|
| 12 | 12 | ||
| 13 | 13 | let mut b: i32 = 0; |
|
| 14 | - | set(&mut b); |
|
| 14 | + | store(&mut b); |
|
| 15 | 15 | ||
| 16 | 16 | return a + b; |
|
| 17 | 17 | } |
test/tests/set.keyword.rad
added
+28 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | //! Test the `set` keyword for assignment statements. |
|
| 3 | + | ||
| 4 | + | record Point { |
|
| 5 | + | x: i32, |
|
| 6 | + | y: i32, |
|
| 7 | + | } |
|
| 8 | + | ||
| 9 | + | @default fn main() -> i32 { |
|
| 10 | + | let mut x: i32 = 1; |
|
| 11 | + | set x = 2; |
|
| 12 | + | if x <> 2 { |
|
| 13 | + | return 1; |
|
| 14 | + | } |
|
| 15 | + | ||
| 16 | + | let mut p: Point = Point { x: 0, y: 0 }; |
|
| 17 | + | set p.x = 10; |
|
| 18 | + | if p.x <> 10 { |
|
| 19 | + | return 1; |
|
| 20 | + | } |
|
| 21 | + | ||
| 22 | + | let mut a: [i32; 3] = [0, 0, 0]; |
|
| 23 | + | set a[1] += 5; |
|
| 24 | + | if a[1] <> 5 { |
|
| 25 | + | return 1; |
|
| 26 | + | } |
|
| 27 | + | return 0; |
|
| 28 | + | } |
test/tests/trait.dispatch.rad
+3 -3
| 2 | 2 | n: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Ops { |
|
| 6 | 6 | fn (*Ops) get() -> i32; |
|
| 7 | - | fn (*mut Ops) set(n: i32); |
|
| 7 | + | fn (*mut Ops) put(n: i32); |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | instance Ops for Acc { |
|
| 11 | 11 | fn (a: *Acc) get() -> i32 { |
|
| 12 | 12 | return a.n; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn (a: *mut Acc) set(n: i32) { |
|
| 15 | + | fn (a: *mut Acc) put(n: i32) { |
|
| 16 | 16 | a.n = n; |
|
| 17 | 17 | } |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | fn dispatch(o: *mut opaque Ops) -> i32 { |
|
| 21 | - | o.set(42); |
|
| 21 | + | o.put(42); |
|
| 22 | 22 | return o.get(); |
|
| 23 | 23 | } |
test/tests/trait.dispatch.ril
+2 -2
| 1 | 1 | data $"vtable::Acc::Ops" align 8 { |
|
| 2 | 2 | fn $"Acc::get"; |
|
| 3 | - | fn $"Acc::set"; |
|
| 3 | + | fn $"Acc::put"; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | fn w32 $"Acc::get"(w64 %0) { |
|
| 7 | 7 | @entry0 |
|
| 8 | 8 | sload w32 %1 %0 0; |
|
| 9 | 9 | ret %1; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | fn w64 $"Acc::set"(w64 %0, w32 %1) { |
|
| 12 | + | fn w64 $"Acc::put"(w64 %0, w32 %1) { |
|
| 13 | 13 | @entry0 |
|
| 14 | 14 | store w32 %1 %0 0; |
|
| 15 | 15 | ret; |
|
| 16 | 16 | } |
|
| 17 | 17 |
vim/radiance.vim
+1 -1
| 15 | 15 | syntax keyword radianceTodo TODO FIXME contained containedin=radianceComment |
|
| 16 | 16 | ||
| 17 | 17 | " Keywords |
|
| 18 | 18 | syntax keyword radianceKeyword mod fn return if else while true false and or not case align static |
|
| 19 | 19 | syntax keyword radianceKeyword export break continue use loop in for match nil undefined |
|
| 20 | - | syntax keyword radianceKeyword let mut as register device constant log record union trait instance |
|
| 20 | + | syntax keyword radianceKeyword let mut set as register device constant log record union trait instance |
|
| 21 | 21 | syntax keyword radianceKeyword throws throw try catch panic assert super |
|
| 22 | 22 | syntax keyword radianceType i8 i16 i32 i64 u8 u16 u32 u64 f32 void bool bit opaque |
|
| 23 | 23 | ||
| 24 | 24 | " Double-quoted strings |
|
| 25 | 25 | syntax region radianceString start=/"/ skip=/\\"/ end=/"/ contains=radianceEscape |