compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
collect.rad
7.2 KiB
decodeTests.rad
14.9 KiB
program.rad
10.8 KiB
reader.rad
14.0 KiB
tests.rad
13.4 KiB
writer.rad
12.7 KiB
binary.rad
5.9 KiB
printer.rad
15.4 KiB
module/
parser/
resolver/
scanner/
alloc.rad
5.3 KiB
ast.rad
23.6 KiB
gen.rad
513 B
il.rad
17.0 KiB
lower.rad
278.5 KiB
module.rad
13.5 KiB
package.rad
1.3 KiB
parser.rad
79.6 KiB
resolver.rad
329.1 KiB
scanner.rad
17.6 KiB
sexpr.rad
6.4 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.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
10.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
551 B
lib/std/lang/il/binary/writer.rad
raw
| 1 | //! Bounded binary RIL encoding into caller-owned storage. |
| 2 | //! After an error, discard bytes written by the failed operation. |
| 3 | |
| 4 | use std::lang::il; |
| 5 | use std::mem; |
| 6 | use std::lang::il::binary; |
| 7 | |
| 8 | /// Output cursor and the symbol table used by this package. |
| 9 | /// The output buffer and symbol table must remain valid until the last write. |
| 10 | export record Writer: Copy { |
| 11 | /// Destination bytes. |
| 12 | bytes: *unsafe mut [u8], |
| 13 | /// Number of bytes written. |
| 14 | offset: u32, |
| 15 | /// Names in wire-index order. The caller supplies unique names. |
| 16 | symbols: *unsafe [*[u8]], |
| 17 | } |
| 18 | |
| 19 | /// Create an output cursor without allocation. |
| 20 | /// The caller must retain exclusive access to the output buffer. |
| 21 | export unsafe fn new(bytes: &mut [u8], symbols: *unsafe [*[u8]]) -> Writer { |
| 22 | return Writer { bytes: bytes as *unsafe mut [u8], offset: 0, symbols }; |
| 23 | } |
| 24 | |
| 25 | /// Write the low bytes of an integer with width 1, 2, 4, or 8. |
| 26 | export unsafe fn integer(out: &mut Writer, value: u64, width: u32) throws (binary::Error) { |
| 27 | if width <> 1 and width <> 2 and width <> 4 and width <> 8 { |
| 28 | throw binary::Error::Invalid; |
| 29 | } |
| 30 | if out.offset > out.bytes.len or width > out.bytes.len - out.offset { |
| 31 | throw binary::Error::Capacity; |
| 32 | } |
| 33 | for i in 0..width { |
| 34 | set out.bytes[out.offset + i] = (value >> (i * 8) as u64) as u8; |
| 35 | } |
| 36 | set out.offset += width; |
| 37 | } |
| 38 | |
| 39 | /// Write a byte sequence with its u32 length. |
| 40 | export unsafe fn bytes(out: &mut Writer, value: &[u8]) throws (binary::Error) { |
| 41 | try integer(out, value.len as u64, 4); |
| 42 | if value.len > out.bytes.len - out.offset { |
| 43 | throw binary::Error::Capacity; |
| 44 | } |
| 45 | for b, i in value { |
| 46 | set out.bytes[out.offset + i] = b; |
| 47 | } |
| 48 | set out.offset += value.len; |
| 49 | } |
| 50 | |
| 51 | /// Write the index of a name in the package symbol table. |
| 52 | export unsafe fn symbol(out: &mut Writer, name: &[u8]) throws (binary::Error) { |
| 53 | for candidate, i in out.symbols { |
| 54 | if mem::eq(candidate, name) { |
| 55 | try integer(out, i as u64, 4); |
| 56 | return; |
| 57 | } |
| 58 | } |
| 59 | throw binary::Error::Symbol; |
| 60 | } |
| 61 | |
| 62 | /// Write an IL type as its byte width. |
| 63 | export unsafe fn typ(out: &mut Writer, value: il::Type) throws (binary::Error) { |
| 64 | try integer(out, il::typeSize(value) as u64, 1); |
| 65 | } |
| 66 | |
| 67 | /// Write a tagged value. Symbol addresses use table indices. |
| 68 | export unsafe fn val(out: &mut Writer, value: il::Val) throws (binary::Error) { |
| 69 | match value { |
| 70 | case il::Val::Reg(reg) => { |
| 71 | try integer(out, super::VALUE_REG as u64, 1); |
| 72 | try integer(out, reg.n as u64, 4); |
| 73 | }, |
| 74 | case il::Val::Imm(n) => { |
| 75 | try integer(out, super::VALUE_IMM as u64, 1); |
| 76 | try integer(out, n as u64, 8); |
| 77 | }, |
| 78 | case il::Val::DataSym(name) => { |
| 79 | try integer(out, super::VALUE_DATASYM as u64, 1); |
| 80 | try symbol(out, name); |
| 81 | }, |
| 82 | case il::Val::FnAddr(name) => { |
| 83 | try integer(out, super::VALUE_FNADDR as u64, 1); |
| 84 | try symbol(out, name); |
| 85 | }, |
| 86 | case il::Val::Undef => try integer(out, super::VALUE_UNDEF as u64, 1), |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /// Write a sequence of values. |
| 91 | export unsafe fn values(out: &mut Writer, items: &[il::Val]) throws (binary::Error) { |
| 92 | try integer(out, items.len as u64, 4); |
| 93 | for item in items { |
| 94 | try val(out, item); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | /// Write an explicit bin operation tag. |
| 99 | unsafe fn binOp(out: &mut Writer, value: il::BinOp) throws (binary::Error) { |
| 100 | match value { |
| 101 | case il::BinOp::Add => try integer(out, super::BIN_ADD as u64, 1), |
| 102 | case il::BinOp::Sub => try integer(out, super::BIN_SUB as u64, 1), |
| 103 | case il::BinOp::Mul => try integer(out, super::BIN_MUL as u64, 1), |
| 104 | case il::BinOp::Sdiv => try integer(out, super::BIN_SDIV as u64, 1), |
| 105 | case il::BinOp::Udiv => try integer(out, super::BIN_UDIV as u64, 1), |
| 106 | case il::BinOp::Srem => try integer(out, super::BIN_SREM as u64, 1), |
| 107 | case il::BinOp::Urem => try integer(out, super::BIN_UREM as u64, 1), |
| 108 | case il::BinOp::Eq => try integer(out, super::BIN_EQ as u64, 1), |
| 109 | case il::BinOp::Ne => try integer(out, super::BIN_NE as u64, 1), |
| 110 | case il::BinOp::Slt => try integer(out, super::BIN_SLT as u64, 1), |
| 111 | case il::BinOp::Sge => try integer(out, super::BIN_SGE as u64, 1), |
| 112 | case il::BinOp::Ult => try integer(out, super::BIN_ULT as u64, 1), |
| 113 | case il::BinOp::Uge => try integer(out, super::BIN_UGE as u64, 1), |
| 114 | case il::BinOp::And => try integer(out, super::BIN_AND as u64, 1), |
| 115 | case il::BinOp::Or => try integer(out, super::BIN_OR as u64, 1), |
| 116 | case il::BinOp::Xor => try integer(out, super::BIN_XOR as u64, 1), |
| 117 | case il::BinOp::Shl => try integer(out, super::BIN_SHL as u64, 1), |
| 118 | case il::BinOp::Sshr => try integer(out, super::BIN_SSHR as u64, 1), |
| 119 | case il::BinOp::Ushr => try integer(out, super::BIN_USHR as u64, 1), |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | /// Write an explicit un operation tag. |
| 124 | unsafe fn unOp(out: &mut Writer, value: il::UnOp) throws (binary::Error) { |
| 125 | match value { |
| 126 | case il::UnOp::Neg => try integer(out, super::UN_NEG as u64, 1), |
| 127 | case il::UnOp::Not => try integer(out, super::UN_NOT as u64, 1), |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | /// Write an explicit cmp operation tag. |
| 132 | unsafe fn cmpOp(out: &mut Writer, value: il::CmpOp) throws (binary::Error) { |
| 133 | match value { |
| 134 | case il::CmpOp::Eq => try integer(out, super::CMP_EQ as u64, 1), |
| 135 | case il::CmpOp::Ne => try integer(out, super::CMP_NE as u64, 1), |
| 136 | case il::CmpOp::Slt => try integer(out, super::CMP_SLT as u64, 1), |
| 137 | case il::CmpOp::Ult => try integer(out, super::CMP_ULT as u64, 1), |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /// Write one instruction. Fields follow the IL record declaration order. |
| 142 | export unsafe fn instr(out: &mut Writer, item: il::Instr) throws (binary::Error) { |
| 143 | match item { |
| 144 | case il::Instr::Reserve { dst: vdst, size: vsize, alignment: valignment } => { |
| 145 | try integer(out, super::INSTR_RESERVE as u64, 1); |
| 146 | try integer(out, vdst.n as u64, 4); |
| 147 | try val(out, vsize); |
| 148 | try integer(out, valignment as u64, 4); |
| 149 | }, |
| 150 | case il::Instr::Load { typ: vtyp, dst: vdst, src: vsrc, offset: voffset } => { |
| 151 | try integer(out, super::INSTR_LOAD as u64, 1); |
| 152 | try typ(out, vtyp); |
| 153 | try integer(out, vdst.n as u64, 4); |
| 154 | try integer(out, vsrc.n as u64, 4); |
| 155 | try integer(out, voffset as u64, 4); |
| 156 | }, |
| 157 | case il::Instr::Sload { typ: vtyp, dst: vdst, src: vsrc, offset: voffset } => { |
| 158 | try integer(out, super::INSTR_SLOAD as u64, 1); |
| 159 | try typ(out, vtyp); |
| 160 | try integer(out, vdst.n as u64, 4); |
| 161 | try integer(out, vsrc.n as u64, 4); |
| 162 | try integer(out, voffset as u64, 4); |
| 163 | }, |
| 164 | case il::Instr::Store { typ: vtyp, src: vsrc, dst: vdst, offset: voffset } => { |
| 165 | try integer(out, super::INSTR_STORE as u64, 1); |
| 166 | try typ(out, vtyp); |
| 167 | try val(out, vsrc); |
| 168 | try integer(out, vdst.n as u64, 4); |
| 169 | try integer(out, voffset as u64, 4); |
| 170 | }, |
| 171 | case il::Instr::Blit { dst: vdst, src: vsrc, size: vsize } => { |
| 172 | try integer(out, super::INSTR_BLIT as u64, 1); |
| 173 | try integer(out, vdst.n as u64, 4); |
| 174 | try integer(out, vsrc.n as u64, 4); |
| 175 | try val(out, vsize); |
| 176 | }, |
| 177 | case il::Instr::Copy { dst: vdst, val: vval } => { |
| 178 | try integer(out, super::INSTR_COPY as u64, 1); |
| 179 | try integer(out, vdst.n as u64, 4); |
| 180 | try val(out, vval); |
| 181 | }, |
| 182 | case il::Instr::BinOp { op: vop, typ: vtyp, dst: vdst, a: va, b: vb } => { |
| 183 | try integer(out, super::INSTR_BINOP as u64, 1); |
| 184 | try binOp(out, vop); |
| 185 | try typ(out, vtyp); |
| 186 | try integer(out, vdst.n as u64, 4); |
| 187 | try val(out, va); |
| 188 | try val(out, vb); |
| 189 | }, |
| 190 | case il::Instr::UnOp { op: vop, typ: vtyp, dst: vdst, a: va } => { |
| 191 | try integer(out, super::INSTR_UNOP as u64, 1); |
| 192 | try unOp(out, vop); |
| 193 | try typ(out, vtyp); |
| 194 | try integer(out, vdst.n as u64, 4); |
| 195 | try val(out, va); |
| 196 | }, |
| 197 | case il::Instr::Zext { typ: vtyp, dst: vdst, val: vval } => { |
| 198 | try integer(out, super::INSTR_ZEXT as u64, 1); |
| 199 | try typ(out, vtyp); |
| 200 | try integer(out, vdst.n as u64, 4); |
| 201 | try val(out, vval); |
| 202 | }, |
| 203 | case il::Instr::Sext { typ: vtyp, dst: vdst, val: vval } => { |
| 204 | try integer(out, super::INSTR_SEXT as u64, 1); |
| 205 | try typ(out, vtyp); |
| 206 | try integer(out, vdst.n as u64, 4); |
| 207 | try val(out, vval); |
| 208 | }, |
| 209 | case il::Instr::Call { retTy: vretTy, dst: vdst, func: vfunc, args: vargs } => { |
| 210 | try integer(out, super::INSTR_CALL as u64, 1); |
| 211 | try typ(out, vretTy); |
| 212 | if let present = vdst { |
| 213 | try integer(out, 1, 1); |
| 214 | try integer(out, present.n as u64, 4); |
| 215 | } else { |
| 216 | try integer(out, 0, 1); |
| 217 | } |
| 218 | try val(out, vfunc); |
| 219 | try values(out, vargs); |
| 220 | }, |
| 221 | case il::Instr::Ret { val: vval } => { |
| 222 | try integer(out, super::INSTR_RET as u64, 1); |
| 223 | if let present = vval { |
| 224 | try integer(out, 1, 1); |
| 225 | try val(out, present); |
| 226 | } else { |
| 227 | try integer(out, 0, 1); |
| 228 | } |
| 229 | }, |
| 230 | case il::Instr::Jmp { target: vtarget, args: vargs } => { |
| 231 | try integer(out, super::INSTR_JMP as u64, 1); |
| 232 | try integer(out, vtarget as u64, 4); |
| 233 | try values(out, vargs); |
| 234 | }, |
| 235 | case il::Instr::Br { op: vop, typ: vtyp, a: va, b: vb, thenTarget: vthenTarget, thenArgs: vthenArgs, elseTarget: velseTarget, elseArgs: velseArgs } => { |
| 236 | try integer(out, super::INSTR_BR as u64, 1); |
| 237 | try cmpOp(out, vop); |
| 238 | try typ(out, vtyp); |
| 239 | try val(out, va); |
| 240 | try val(out, vb); |
| 241 | try integer(out, vthenTarget as u64, 4); |
| 242 | try values(out, vthenArgs); |
| 243 | try integer(out, velseTarget as u64, 4); |
| 244 | try values(out, velseArgs); |
| 245 | }, |
| 246 | case il::Instr::Switch { val: vval, defaultTarget: vdefaultTarget, defaultArgs: vdefaultArgs, cases: vcases } => { |
| 247 | try integer(out, super::INSTR_SWITCH as u64, 1); |
| 248 | try val(out, vval); |
| 249 | try integer(out, vdefaultTarget as u64, 4); |
| 250 | try values(out, vdefaultArgs); |
| 251 | try integer(out, vcases.len as u64, 4); |
| 252 | for branch in vcases { |
| 253 | try integer(out, branch.value as u64, 8); |
| 254 | try integer(out, branch.target as u64, 4); |
| 255 | try values(out, branch.args); |
| 256 | } |
| 257 | }, |
| 258 | case il::Instr::Unreachable => { |
| 259 | try integer(out, super::INSTR_UNREACHABLE as u64, 1); |
| 260 | }, |
| 261 | case il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 } => { |
| 262 | try integer(out, super::INSTR_ECALL as u64, 1); |
| 263 | try integer(out, vdst.n as u64, 4); |
| 264 | try val(out, vnum); |
| 265 | try val(out, va0); |
| 266 | try val(out, va1); |
| 267 | try val(out, va2); |
| 268 | try val(out, va3); |
| 269 | }, |
| 270 | case il::Instr::DeviceRead { typ: t, dst, handle, offset } => { |
| 271 | try integer(out, super::INSTR_DEVICE_READ as u64, 1); |
| 272 | try typ(out, t); |
| 273 | try integer(out, dst.n as u64, 4); |
| 274 | try val(out, handle); try val(out, offset); |
| 275 | }, |
| 276 | case il::Instr::DeviceWrite { typ: t, handle, offset, value } => { |
| 277 | try integer(out, super::INSTR_DEVICE_WRITE as u64, 1); |
| 278 | try typ(out, t); |
| 279 | try val(out, handle); try val(out, offset); try val(out, value); |
| 280 | }, |
| 281 | case il::Instr::Ebreak => { |
| 282 | try integer(out, super::INSTR_EBREAK as u64, 1); |
| 283 | }, |
| 284 | case il::Instr::MemoryFence => { |
| 285 | try integer(out, super::INSTR_MEMORYFENCE as u64, 1); |
| 286 | }, |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | /// Write one data initializer and its repetition count. |
| 291 | export unsafe fn dataValue(out: &mut Writer, value: il::DataValue) throws (binary::Error) { |
| 292 | match value.item { |
| 293 | case il::DataItem::Val { typ: t, val: n } => { |
| 294 | try integer(out, super::DATA_VAL as u64, 1); |
| 295 | try typ(out, t); |
| 296 | try integer(out, n as u64, il::typeSize(t)); |
| 297 | }, |
| 298 | case il::DataItem::Sym(name) => { |
| 299 | try integer(out, super::DATA_SYM as u64, 1); |
| 300 | try symbol(out, name); |
| 301 | }, |
| 302 | case il::DataItem::Fn(name) => { |
| 303 | try integer(out, super::DATA_FN as u64, 1); |
| 304 | try symbol(out, name); |
| 305 | }, |
| 306 | case il::DataItem::Str(text) => { |
| 307 | try integer(out, super::DATA_STR as u64, 1); |
| 308 | try bytes(out, text); |
| 309 | }, |
| 310 | case il::DataItem::Undef => try integer(out, super::DATA_UNDEF as u64, 1), |
| 311 | } |
| 312 | try integer(out, value.count as u64, 4); |
| 313 | } |