compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
instructions.rad
14.7 KiB
records.rad
8.3 KiB
structure.rad
7.6 KiB
tests.rad
2.8 KiB
images/
binary.rad
13.9 KiB
images.rad
10.6 KiB
printer.rad
14.7 KiB
module/
parser/
resolver/
scanner/
alloc.rad
4.3 KiB
ast.rad
23.3 KiB
gen.rad
513 B
il.rad
16.1 KiB
lower.rad
272.5 KiB
module.rad
13.2 KiB
package.rad
1.3 KiB
parser.rad
77.8 KiB
resolver.rad
308.6 KiB
scanner.rad
17.5 KiB
sexpr.rad
6.3 KiB
strings.rad
2.2 KiB
types.rad
286 B
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.1 KiB
intrinsics.rad
467 B
io.rad
1.3 KiB
lang.rad
276 B
mem.rad
2.2 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.2 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
351 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
3.8 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.4 KiB
std.lib.test
380 B
lib/std/lang/il/binary/instructions.rad
raw
| 1 | //! Explicit binary RIL instruction tags and operand payloads. |
| 2 | //! |
| 3 | //! Type tags are byte widths 1/2/4/8. Value tags are Reg=0, Imm=1, |
| 4 | //! DataSym=2, FnAddr=3, Undef=4. Instruction tags are Reserve=0, Load=1, |
| 5 | //! Sload=2, Store=3, Blit=4, Copy=5, BinOp=6, UnOp=7, Zext=8, Sext=9, |
| 6 | //! Call=10, Ret=11, Jmp=12, Br=13, Switch=14, Unreachable=15, Ecall=16, |
| 7 | //! Ebreak=17, MemoryFence=18. Payload fields follow the shared IL declaration |
| 8 | //! order, except BinOp/UnOp put their operation byte first. Optional operands |
| 9 | //! use a canonical 0/1 byte followed by the payload only when present. |
| 10 | |
| 11 | use std::lang::il; |
| 12 | |
| 13 | /// Write one logical word width. |
| 14 | export fn putType(w: *mut super::Writer, typ: il::Type) throws (super::Error) { |
| 15 | try super::put8(w, il::typeSize(typ) as u8); |
| 16 | } |
| 17 | |
| 18 | /// Read one logical word width, rejecting unknown tags. |
| 19 | export fn getType(r: *mut super::Reader) -> il::Type throws (super::Error) { |
| 20 | let offset = r.offset; |
| 21 | match try super::get8(r) { |
| 22 | case 1 => return il::Type::W8, |
| 23 | case 2 => return il::Type::W16, |
| 24 | case 4 => return il::Type::W32, |
| 25 | case 8 => return il::Type::W64, |
| 26 | else => throw super::error(offset, "invalid RIL word width"), |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | /// Write a typed value without conflating integers and symbolic addresses. |
| 31 | export fn putVal(w: *mut super::Writer, val: il::Val) throws (super::Error) { |
| 32 | match val { |
| 33 | case il::Val::Reg(reg) => { try super::put8(w, 0); try super::put32(w, reg.n); } |
| 34 | case il::Val::Imm(value) => { try super::put8(w, 1); try super::put64(w, value as u64); } |
| 35 | case il::Val::DataSym(name) => { try super::put8(w, 2); try super::putSymbol(w, name, 1); } |
| 36 | case il::Val::FnAddr(name) => { try super::put8(w, 3); try super::putSymbol(w, name, 2); } |
| 37 | case il::Val::Undef => try super::put8(w, 4), |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | /// Read a typed value; no raw integer is ever interpreted as a host pointer. |
| 42 | export fn getVal(r: *mut super::Reader) -> il::Val throws (super::Error) { |
| 43 | let offset = r.offset; |
| 44 | match try super::get8(r) { |
| 45 | case 0 => return il::Val::Reg(try getReg(r)), |
| 46 | case 1 => return il::Val::Imm((try super::get64(r)) as i64), |
| 47 | case 2 => return il::Val::DataSym(try super::getSymbol(r, 1)), |
| 48 | case 3 => return il::Val::FnAddr(try super::getSymbol(r, 2)), |
| 49 | case 4 => return il::Val::Undef, |
| 50 | else => throw super::error(offset, "invalid RIL value tag"), |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /// Read an SSA register index for the shared allocator. |
| 55 | export fn getReg(r: *mut super::Reader) -> il::Reg throws (super::Error) { |
| 56 | return il::Reg { n: try super::get32(r) }; |
| 57 | } |
| 58 | |
| 59 | /// Write a length-prefixed argument list. |
| 60 | export fn putArgs(w: *mut super::Writer, args: *[il::Val]) throws (super::Error) { |
| 61 | try super::put32(w, args.len); |
| 62 | for arg in args { try putVal(w, arg); } |
| 63 | } |
| 64 | |
| 65 | /// Read a length-prefixed argument list into caller-owned mutable storage. |
| 66 | export fn getArgs(r: *mut super::Reader) -> *mut [il::Val] throws (super::Error) { |
| 67 | let count = try super::count(r, 1); |
| 68 | let args = try super::storage(r.arena, @sizeOf(il::Val), @alignOf(il::Val), count, r.offset) as *mut [il::Val]; |
| 69 | for i in 0..count { set args[i] = try getVal(r); } |
| 70 | return args; |
| 71 | } |
| 72 | |
| 73 | /// Stable binary ALU tags: arithmetic 0..6, comparisons 7..12, bitwise 13..18. |
| 74 | fn binTag(op: il::BinOp) -> u8 { |
| 75 | match op { |
| 76 | case il::BinOp::Add => return 0, |
| 77 | case il::BinOp::Sub => return 1, |
| 78 | case il::BinOp::Mul => return 2, |
| 79 | case il::BinOp::Sdiv => return 3, |
| 80 | case il::BinOp::Udiv => return 4, |
| 81 | case il::BinOp::Srem => return 5, |
| 82 | case il::BinOp::Urem => return 6, |
| 83 | case il::BinOp::Eq => return 7, |
| 84 | case il::BinOp::Ne => return 8, |
| 85 | case il::BinOp::Slt => return 9, |
| 86 | case il::BinOp::Sge => return 10, |
| 87 | case il::BinOp::Ult => return 11, |
| 88 | case il::BinOp::Uge => return 12, |
| 89 | case il::BinOp::And => return 13, |
| 90 | case il::BinOp::Or => return 14, |
| 91 | case il::BinOp::Xor => return 15, |
| 92 | case il::BinOp::Shl => return 16, |
| 93 | case il::BinOp::Sshr => return 17, |
| 94 | case il::BinOp::Ushr => return 18, |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | /// Decode a binary ALU operation without depending on union layout. |
| 99 | fn getBinOp(r: *mut super::Reader) -> il::BinOp throws (super::Error) { |
| 100 | let offset = r.offset; |
| 101 | match try super::get8(r) { |
| 102 | case 0 => return il::BinOp::Add, |
| 103 | case 1 => return il::BinOp::Sub, |
| 104 | case 2 => return il::BinOp::Mul, |
| 105 | case 3 => return il::BinOp::Sdiv, |
| 106 | case 4 => return il::BinOp::Udiv, |
| 107 | case 5 => return il::BinOp::Srem, |
| 108 | case 6 => return il::BinOp::Urem, |
| 109 | case 7 => return il::BinOp::Eq, |
| 110 | case 8 => return il::BinOp::Ne, |
| 111 | case 9 => return il::BinOp::Slt, |
| 112 | case 10 => return il::BinOp::Sge, |
| 113 | case 11 => return il::BinOp::Ult, |
| 114 | case 12 => return il::BinOp::Uge, |
| 115 | case 13 => return il::BinOp::And, |
| 116 | case 14 => return il::BinOp::Or, |
| 117 | case 15 => return il::BinOp::Xor, |
| 118 | case 16 => return il::BinOp::Shl, |
| 119 | case 17 => return il::BinOp::Sshr, |
| 120 | case 18 => return il::BinOp::Ushr, |
| 121 | else => throw super::error(offset, "invalid RIL binary operation tag"), |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | /// Stable compare-and-branch tags: Eq=0, Ne=1, Slt=2, Ult=3. |
| 126 | fn cmpTag(op: il::CmpOp) -> u8 { |
| 127 | match op { |
| 128 | case il::CmpOp::Eq => return 0, |
| 129 | case il::CmpOp::Ne => return 1, |
| 130 | case il::CmpOp::Slt => return 2, |
| 131 | case il::CmpOp::Ult => return 3, |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | /// Decode one compare-and-branch operation. |
| 136 | fn getCmpOp(r: *mut super::Reader) -> il::CmpOp throws (super::Error) { |
| 137 | let offset = r.offset; |
| 138 | match try super::get8(r) { |
| 139 | case 0 => return il::CmpOp::Eq, |
| 140 | case 1 => return il::CmpOp::Ne, |
| 141 | case 2 => return il::CmpOp::Slt, |
| 142 | case 3 => return il::CmpOp::Ult, |
| 143 | else => throw super::error(offset, "invalid RIL comparison tag"), |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /// Read a nonzero power-of-two memory alignment. |
| 148 | export fn getAlignment(r: *mut super::Reader) -> u32 throws (super::Error) { |
| 149 | let offset = r.offset; |
| 150 | let alignment = try super::get32(r); |
| 151 | if alignment == 0 or (alignment & (alignment - 1)) <> 0 { |
| 152 | throw super::error(offset, "RIL alignment must be a nonzero power of two"); |
| 153 | } |
| 154 | return alignment; |
| 155 | } |
| 156 | |
| 157 | /// Serialize every existing instruction variant with explicit field encodings. |
| 158 | export fn put(w: *mut super::Writer, instr: il::Instr) throws (super::Error) { |
| 159 | match instr { |
| 160 | case il::Instr::Reserve { dst, size, alignment } => { |
| 161 | try super::put8(w, 0); try super::put32(w, dst.n); |
| 162 | try putVal(w, size); try super::put32(w, alignment); |
| 163 | } |
| 164 | case il::Instr::Load { typ, dst, src, offset } => { |
| 165 | try super::put8(w, 1); try putType(w, typ); try super::put32(w, dst.n); |
| 166 | try super::put32(w, src.n); try super::put32(w, offset as u32); |
| 167 | } |
| 168 | case il::Instr::Sload { typ, dst, src, offset } => { |
| 169 | try super::put8(w, 2); try putType(w, typ); try super::put32(w, dst.n); |
| 170 | try super::put32(w, src.n); try super::put32(w, offset as u32); |
| 171 | } |
| 172 | case il::Instr::Store { typ, src, dst, offset } => { |
| 173 | try super::put8(w, 3); try putType(w, typ); try putVal(w, src); |
| 174 | try super::put32(w, dst.n); try super::put32(w, offset as u32); |
| 175 | } |
| 176 | case il::Instr::Blit { dst, src, size, alignment } => { |
| 177 | try super::put8(w, 4); try super::put32(w, dst.n); try super::put32(w, src.n); |
| 178 | try putVal(w, size); try super::put32(w, alignment); |
| 179 | } |
| 180 | case il::Instr::Copy { dst, val } => { |
| 181 | try super::put8(w, 5); try super::put32(w, dst.n); try putVal(w, val); |
| 182 | } |
| 183 | case il::Instr::BinOp { op, typ, dst, a, b } => { |
| 184 | try super::put8(w, 6); try super::put8(w, binTag(op)); try putType(w, typ); |
| 185 | try super::put32(w, dst.n); try putVal(w, a); try putVal(w, b); |
| 186 | } |
| 187 | case il::Instr::UnOp { op, typ, dst, a } => { |
| 188 | try super::put8(w, 7); |
| 189 | match op { case il::UnOp::Neg => try super::put8(w, 0), case il::UnOp::Not => try super::put8(w, 1) } |
| 190 | try putType(w, typ); try super::put32(w, dst.n); try putVal(w, a); |
| 191 | } |
| 192 | case il::Instr::Zext { typ, dst, val } => { |
| 193 | try super::put8(w, 8); try putType(w, typ); try super::put32(w, dst.n); try putVal(w, val); |
| 194 | } |
| 195 | case il::Instr::Sext { typ, dst, val } => { |
| 196 | try super::put8(w, 9); try putType(w, typ); try super::put32(w, dst.n); try putVal(w, val); |
| 197 | } |
| 198 | case il::Instr::Call { retTy, dst, func, args } => { |
| 199 | try super::put8(w, 10); try putType(w, retTy); |
| 200 | if let reg = dst { try super::put8(w, 1); try super::put32(w, reg.n); } |
| 201 | else { try super::put8(w, 0); } |
| 202 | try putVal(w, func); try putArgs(w, args); |
| 203 | } |
| 204 | case il::Instr::Ret { val } => { |
| 205 | try super::put8(w, 11); |
| 206 | if let value = val { try super::put8(w, 1); try putVal(w, value); } |
| 207 | else { try super::put8(w, 0); } |
| 208 | } |
| 209 | case il::Instr::Jmp { target, args } => { |
| 210 | try super::put8(w, 12); try super::put32(w, target); try putArgs(w, args); |
| 211 | } |
| 212 | case il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs } => { |
| 213 | try super::put8(w, 13); try super::put8(w, cmpTag(op)); try putType(w, typ); |
| 214 | try putVal(w, a); try putVal(w, b); try super::put32(w, thenTarget); |
| 215 | try putArgs(w, thenArgs); try super::put32(w, elseTarget); try putArgs(w, elseArgs); |
| 216 | } |
| 217 | case il::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
| 218 | try super::put8(w, 14); try putVal(w, val); try super::put32(w, defaultTarget); |
| 219 | try putArgs(w, defaultArgs); try super::put32(w, cases.len); |
| 220 | for item in cases { |
| 221 | try super::put64(w, item.value as u64); try super::put32(w, item.target); try putArgs(w, item.args); |
| 222 | } |
| 223 | } |
| 224 | case il::Instr::Unreachable => try super::put8(w, 15), |
| 225 | case il::Instr::Ecall { dst, num, a0, a1, a2, a3 } => { |
| 226 | try super::put8(w, 16); try super::put32(w, dst.n); try putVal(w, num); |
| 227 | try putVal(w, a0); try putVal(w, a1); try putVal(w, a2); try putVal(w, a3); |
| 228 | } |
| 229 | case il::Instr::Ebreak => try super::put8(w, 17), |
| 230 | case il::Instr::MemoryFence => try super::put8(w, 18), |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /// Decode every current instruction variant. Sequential locals make wire order |
| 235 | /// independent of record-literal expression evaluation and compiler ABI layout. |
| 236 | export fn get(r: *mut super::Reader) -> il::Instr throws (super::Error) { |
| 237 | let offset = r.offset; |
| 238 | let tag = try super::get8(r); |
| 239 | match tag { |
| 240 | case 0 => { |
| 241 | let dst = try getReg(r); let size = try getVal(r); let alignment = try getAlignment(r); |
| 242 | return il::Instr::Reserve { dst, size, alignment }; |
| 243 | } |
| 244 | case 1, 2 => { |
| 245 | let typ = try getType(r); let dst = try getReg(r); let src = try getReg(r); |
| 246 | let offset = (try super::get32(r)) as i32; |
| 247 | if tag == 1 { return il::Instr::Load { typ, dst, src, offset }; } |
| 248 | return il::Instr::Sload { typ, dst, src, offset }; |
| 249 | } |
| 250 | case 3 => { |
| 251 | let typ = try getType(r); let src = try getVal(r); let dst = try getReg(r); |
| 252 | let offset = (try super::get32(r)) as i32; |
| 253 | return il::Instr::Store { typ, src, dst, offset }; |
| 254 | } |
| 255 | case 4 => { |
| 256 | let dst = try getReg(r); let src = try getReg(r); let size = try getVal(r); |
| 257 | let alignment = try getAlignment(r); |
| 258 | return il::Instr::Blit { dst, src, size, alignment }; |
| 259 | } |
| 260 | case 5 => { |
| 261 | let dst = try getReg(r); let val = try getVal(r); |
| 262 | return il::Instr::Copy { dst, val }; |
| 263 | } |
| 264 | case 6 => { |
| 265 | let op = try getBinOp(r); let typ = try getType(r); let dst = try getReg(r); |
| 266 | let a = try getVal(r); let b = try getVal(r); |
| 267 | return il::Instr::BinOp { op, typ, dst, a, b }; |
| 268 | } |
| 269 | case 7 => { |
| 270 | let opOffset = r.offset; let opTag = try super::get8(r); |
| 271 | if opTag > 1 { throw super::error(opOffset, "invalid RIL unary operation tag"); } |
| 272 | let op = il::UnOp::Neg if opTag == 0 else il::UnOp::Not; |
| 273 | let typ = try getType(r); let dst = try getReg(r); let a = try getVal(r); |
| 274 | return il::Instr::UnOp { op, typ, dst, a }; |
| 275 | } |
| 276 | case 8, 9 => { |
| 277 | let typ = try getType(r); let dst = try getReg(r); let val = try getVal(r); |
| 278 | if tag == 8 { return il::Instr::Zext { typ, dst, val }; } |
| 279 | return il::Instr::Sext { typ, dst, val }; |
| 280 | } |
| 281 | case 10 => { |
| 282 | let retTy = try getType(r); let present = try super::getBool(r); |
| 283 | let mut dst: ?il::Reg = nil; |
| 284 | if present { set dst = try getReg(r); } |
| 285 | let func = try getVal(r); let args = try getArgs(r); |
| 286 | return il::Instr::Call { retTy, dst, func, args }; |
| 287 | } |
| 288 | case 11 => { |
| 289 | let present = try super::getBool(r); |
| 290 | let mut val: ?il::Val = nil; |
| 291 | if present { set val = try getVal(r); } |
| 292 | return il::Instr::Ret { val }; |
| 293 | } |
| 294 | case 12 => { |
| 295 | let target = try super::get32(r); let args = try getArgs(r); |
| 296 | return il::Instr::Jmp { target, args }; |
| 297 | } |
| 298 | case 13 => { |
| 299 | let op = try getCmpOp(r); let typ = try getType(r); |
| 300 | let a = try getVal(r); let b = try getVal(r); |
| 301 | let thenTarget = try super::get32(r); let thenArgs = try getArgs(r); |
| 302 | let elseTarget = try super::get32(r); let elseArgs = try getArgs(r); |
| 303 | return il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs }; |
| 304 | } |
| 305 | case 14 => { |
| 306 | let val = try getVal(r); let defaultTarget = try super::get32(r); let defaultArgs = try getArgs(r); |
| 307 | let count = try super::count(r, 16); |
| 308 | let cases = try super::storage(r.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), count, r.offset) as *mut [il::SwitchCase]; |
| 309 | for i in 0..count { |
| 310 | let value = (try super::get64(r)) as i64; let target = try super::get32(r); let args = try getArgs(r); |
| 311 | set cases[i] = il::SwitchCase { value, target, args }; |
| 312 | } |
| 313 | return il::Instr::Switch { val, defaultTarget, defaultArgs, cases }; |
| 314 | } |
| 315 | case 15 => return il::Instr::Unreachable, |
| 316 | case 16 => { |
| 317 | let dst = try getReg(r); let num = try getVal(r); let a0 = try getVal(r); |
| 318 | let a1 = try getVal(r); let a2 = try getVal(r); let a3 = try getVal(r); |
| 319 | return il::Instr::Ecall { dst, num, a0, a1, a2, a3 }; |
| 320 | } |
| 321 | case 17 => return il::Instr::Ebreak, |
| 322 | case 18 => return il::Instr::MemoryFence, |
| 323 | else => throw super::error(offset, "invalid RIL instruction tag"), |
| 324 | } |
| 325 | } |