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.9 KiB
reader.rad
14.1 KiB
tests.rad
13.7 KiB
writer.rad
12.7 KiB
binary.rad
5.9 KiB
printer.rad
15.4 KiB
tests.rad
3.8 KiB
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.7 KiB
gen.rad
513 B
il.rad
19.5 KiB
lower.rad
308.1 KiB
module.rad
14.9 KiB
package.rad
1.3 KiB
parser.rad
89.5 KiB
resolver.rad
439.6 KiB
scanner.rad
18.0 KiB
sexpr.rad
6.4 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
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
9.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 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 | } |