compiler: Format RV64 instructions with checked local buffers

648ff424c6b3a79aed6bb7e37e69f66ef6d5c18acdd58a1e73740df6616dca70
Alexis Sellier committed ago 1 parent 59c7667a
compiler/radiance.rad +1 -1
1327 1327
        let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions {
1328 1328
            logPath: nil,
1329 1329
            debug: false,
1330 1330
            entryMode: CodegenEntryMode::None,
1331 1331
        });
1332 -
        printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs, res.arena);
1332 +
        printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs);
1333 1333
        io::print("\n");
1334 1334
1335 1335
        return;
1336 1336
    }
1337 1337
    // Generate binary output if path specified.
lib/std/arch/rv64/asm/tests.rad +19 -12
13 13
14 14
static ASM_ARENA_STORAGE: [u8; 65536] = [0; 65536];
15 15
static ASM_TEXT_STORAGE: [u32; 256] = [0; 256];
16 16
static ASM_DATA_STORAGE: [u8; 1024] = [0; 1024];
17 17
unsafe static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
18 -
static PRINT_ARENA_STORAGE: [u8; 1024] = [0; 1024];
19 -
static PRINT_BUFFER: [u8; 128] = [0; 128];
20 18
21 19
/// The retired-instruction CSR assembles to its exact architectural encoding.
22 20
@test unsafe fn retiredCounter() throws (testing::TestError) {
23 21
    let program = try assembleSource(".text;\ncsrr %a0 instret;\n");
24 22
    assert program.text.len == 1 and program.text[0] == 0xc0202573;
71 69
        return;
72 70
    };
73 71
    throw testing::TestError::Failed;
74 72
}
75 73
76 -
unsafe fn printInstrText(instr: u32) -> *[u8] {
77 -
    let mut arena = alloc::new(&mut PRINT_ARENA_STORAGE[..]);
78 -
    let mut pos: u32 = 0;
79 -
    let bytes: 'output = &mut PRINT_BUFFER[..] in {
74 +
/// Compare the instruction text with the expected bytes.
75 +
fn expectInstrText(instr: u32, expected: &[u8]) throws (testing::TestError) {
76 +
    let mut buffer: [u8; 128] = [0; 128];
77 +
    let bytes: 'output = &mut buffer[..] in {
80 78
        let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 };
81 -
        printer::printInstr(&mut out, &mut arena, instr);
82 -
        set pos = out.pos;
79 +
        printer::printInstr(&mut out, instr);
80 +
        try testing::expectBytesEq(&out.buf[..out.pos], expected);
83 81
    }
84 -
    return &PRINT_BUFFER[..pos];
85 82
}
86 83
87 84
@test unsafe fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) {
88 85
    let program = try assembleSource(
89 86
        ".text;\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n"
292 289
    try expectAssembleFail(
293 290
        ".text;\ncsrsi mstatus 32;\n"
294 291
    );
295 292
}
296 293
297 -
@test unsafe fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) {
298 -
    let text = printInstrText(encode::addi(rv64::A0, rv64::SP, 42));
299 -
    try testing::expect(mem::eq(text, "addi    %a0, %sp, 42"));
294 +
@test fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) {
295 +
    try expectInstrText(encode::addi(rv64::A0, rv64::SP, 42), "addi    %a0, %sp, 42");
296 +
}
297 +
298 +
/// Immediate operands preserve signs, separators, and address syntax.
299 +
@test fn testSafePrintImmediates() throws (testing::TestError) {
300 +
    try expectInstrText(encode::addi(rv64::A0, rv64::ZERO, -2048), "li      %a0, -2048");
301 +
    try expectInstrText(encode::ld(rv64::A0, rv64::SP, -8), "ld      %a0, -8(%sp)");
302 +
    try expectInstrText(encode::sd(rv64::A0, rv64::SP, 16), "sd      %a0, 16(%sp)");
303 +
    try expectInstrText(encode::beq(rv64::A0, rv64::SP, -4), "beq     %a0, %sp, -4");
304 +
    try expectInstrText(encode::bne(rv64::A0, rv64::ZERO, 4), "bnez    %a0, 4");
305 +
    try expectInstrText(encode::jal(rv64::ZERO, -4), "j       -4");
306 +
    try expectInstrText(0xFFFFFFFF, "unknown(4294967295)");
300 307
}
lib/std/arch/rv64/printer.rad +78 -72
1 1
//! RV64 instruction printer.
2 2
//!
3 3
//! Prints 32-bit instructions in assembly text format.
4 4
5 5
use std::fmt;
6 -
use std::mem;
7 -
use std::lang::alloc;
8 6
use std::lang::gen;
9 7
use std::lang::sexpr;
10 8
use std::lang::gen::types;
11 9
12 10
use super::decode;
42 40
/// Write a string to output.
43 41
fn write(out: &mut opaque sexpr::Output, s: &[u8]) {
44 42
    sexpr::write(out, s);
45 43
}
46 44
47 -
/// Format `i32` into arena.
48 -
unsafe fn formatI32(a: &mut alloc::Arena, val: i32) -> *[u8] {
49 -
    let mut digits: [u8; 12] = undefined;
45 +
/// Write an `i32` before its stack buffer leaves scope.
46 +
fn writeI32(out: &mut opaque sexpr::Output, val: i32) {
47 +
    let mut digits: [u8; 12] = [0; 12];
50 48
    let start = fmt::formatI32(val, &mut digits[..]);
51 -
    let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8];
52 -
    try! mem::copy(slice, &digits[start..]);
53 -
54 -
    return slice;
49 +
    write(out, &digits[start..]);
55 50
}
56 51
57 -
/// Format `u32` into arena.
58 -
unsafe fn formatU32(a: &mut alloc::Arena, val: u32) -> *[u8] {
59 -
    let mut digits: [u8; 10] = undefined;
52 +
/// Write a `u32` before its stack buffer leaves scope.
53 +
fn writeU32(out: &mut opaque sexpr::Output, val: u32) {
54 +
    let mut digits: [u8; 10] = [0; 10];
60 55
    let start = fmt::formatU32(val, &mut digits[..]);
61 -
    let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8];
62 -
    try! mem::copy(slice, &digits[start..]);
63 -
64 -
    return slice;
56 +
    write(out, &digits[start..]);
65 57
}
66 58
67 59
///////////////////////////////
68 60
// Instruction Printing      //
69 61
///////////////////////////////
107 99
    writeMnem(out, m);
108 100
    writeDelim(out, &[regNameR(rd), regNameR(rs1), regNameR(rs2)]);
109 101
}
110 102
111 103
/// I-type: `op rd, rs1, imm`.
112 -
unsafe fn fmtI(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
104 +
fn fmtI(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
113 105
    writeMnem(out, m);
114 -
    writeDelim(out, &[regNameR(rd), regNameR(rs1), formatI32(a, imm)]);
106 +
    writeDelim(out, &[regNameR(rd), regNameR(rs1)]);
107 +
    write(out, ", ");
108 +
    writeI32(out, imm);
115 109
}
116 110
117 111
/// 2-reg: `op rd, rs`.
118 112
fn fmt2R(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) {
119 113
    writeMnem(out, m);
120 114
    writeDelim(out, &[regNameR(rd), regNameR(rs)]);
121 115
}
122 116
123 117
/// reg + imm: `op rd, imm`.
124 -
unsafe fn fmtRI(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) {
118 +
fn fmtRI(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, imm: i32) {
125 119
    writeMnem(out, m);
126 -
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
120 +
    write(out, regNameR(rd));
121 +
    write(out, ", ");
122 +
    writeI32(out, imm);
127 123
}
128 124
129 125
/// imm only: `op imm`.
130 -
unsafe fn fmtImm(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], imm: i32) {
126 +
fn fmtImm(out: &mut opaque sexpr::Output, m: *[u8], imm: i32) {
131 127
    writeMnem(out, m);
132 -
    write(out, formatI32(a, imm));
128 +
    writeI32(out, imm);
133 129
}
134 130
135 131
/// 1-reg: `op rs`.
136 132
fn fmt1R(out: &mut opaque sexpr::Output, m: *[u8], rs: gen::Reg) {
137 133
    writeMnem(out, m);
138 134
    write(out, regNameR(rs));
139 135
}
140 136
141 137
/// Load: `op rd, imm(rs1)`.
142 -
unsafe fn fmtLoad(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
138 +
fn fmtLoad(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
143 139
    writeMnem(out, m);
144 -
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
140 +
    write(out, regNameR(rd));
141 +
    write(out, ", ");
142 +
    writeI32(out, imm);
145 143
    writeParens(out, regNameR(rs1));
146 144
}
147 145
148 146
/// Store: `op rs2, imm(rs1)`.
149 -
unsafe fn fmtStore(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
147 +
fn fmtStore(out: &mut opaque sexpr::Output, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
150 148
    writeMnem(out, m);
151 -
    writeDelim(out, &[regNameR(rs2), formatI32(a, imm)]);
149 +
    write(out, regNameR(rs2));
150 +
    write(out, ", ");
151 +
    writeI32(out, imm);
152 152
    writeParens(out, regNameR(rs1));
153 153
}
154 154
155 155
/// Branch: `op rs1, rs2, imm`.
156 -
unsafe fn fmtB(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
156 +
fn fmtB(out: &mut opaque sexpr::Output, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
157 157
    writeMnem(out, m);
158 -
    writeDelim(out, &[regNameR(rs1), regNameR(rs2), formatI32(a, imm)]);
158 +
    writeDelim(out, &[regNameR(rs1), regNameR(rs2)]);
159 +
    write(out, ", ");
160 +
    writeI32(out, imm);
159 161
}
160 162
161 163
/// Branch zero: `op rs1, imm`.
162 -
unsafe fn fmtBz(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) {
164 +
fn fmtBz(out: &mut opaque sexpr::Output, m: *[u8], rs1: gen::Reg, imm: i32) {
163 165
    writeMnem(out, m);
164 -
    writeDelim(out, &[regNameR(rs1), formatI32(a, imm)]);
166 +
    write(out, regNameR(rs1));
167 +
    write(out, ", ");
168 +
    writeI32(out, imm);
165 169
}
166 170
167 171
/// Print a single instruction to output buffer.
168 -
export unsafe fn printInstr(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, instr: u32) {
172 +
export fn printInstr(out: &mut opaque sexpr::Output, instr: u32) {
169 173
    let decoded = decode::decode(instr);
170 174
171 175
    match decoded {
172 -
        case decode::Instr::Lui { rd, imm } => fmtRI(out, a, "lui", rd, imm),
173 -
        case decode::Instr::Auipc { rd, imm } => fmtRI(out, a, "auipc", rd, imm),
176 +
        case decode::Instr::Lui { rd, imm } => fmtRI(out, "lui", rd, imm),
177 +
        case decode::Instr::Auipc { rd, imm } => fmtRI(out, "auipc", rd, imm),
174 178
        case decode::Instr::Jal { rd, imm } => {
175 179
            if *rd == 0 {
176 -
                fmtImm(out, a, "j", imm);
180 +
                fmtImm(out, "j", imm);
177 181
            } else {
178 -
                fmtRI(out, a, "jal", rd, imm);
182 +
                fmtRI(out, "jal", rd, imm);
179 183
            }
180 184
        },
181 185
        case decode::Instr::Jalr { rd, rs1, imm } => {
182 186
            if *rd == 0 and *rs1 == 1 and imm == 0 {
183 187
                write(out, "ret");
184 188
            } else if *rd == 0 and imm == 0 {
185 189
                fmt1R(out, "jr", rs1);
186 190
            } else {
187 -
                fmtI(out, a, "jalr", rd, rs1, imm);
191 +
                fmtI(out, "jalr", rd, rs1, imm);
188 192
            }
189 193
        },
190 194
        case decode::Instr::Beq { rs1, rs2, imm } => {
191 195
            if *rs2 == 0 {
192 -
                fmtBz(out, a, "beqz", rs1, imm);
196 +
                fmtBz(out, "beqz", rs1, imm);
193 197
            } else {
194 -
                fmtB(out, a, "beq", rs1, rs2, imm);
198 +
                fmtB(out, "beq", rs1, rs2, imm);
195 199
            }
196 200
        },
197 201
        case decode::Instr::Bne { rs1, rs2, imm } => {
198 202
            if *rs2 == 0 {
199 -
                fmtBz(out, a, "bnez", rs1, imm);
203 +
                fmtBz(out, "bnez", rs1, imm);
200 204
            } else {
201 -
                fmtB(out, a, "bne", rs1, rs2, imm);
205 +
                fmtB(out, "bne", rs1, rs2, imm);
202 206
            }
203 207
        },
204 -
        case decode::Instr::Blt { rs1, rs2, imm }  => fmtB(out, a, "blt", rs1, rs2, imm),
205 -
        case decode::Instr::Bge { rs1, rs2, imm }  => fmtB(out, a, "bge", rs1, rs2, imm),
206 -
        case decode::Instr::Bltu { rs1, rs2, imm } => fmtB(out, a, "bltu", rs1, rs2, imm),
207 -
        case decode::Instr::Bgeu { rs1, rs2, imm } => fmtB(out, a, "bgeu", rs1, rs2, imm),
208 -
        case decode::Instr::Lb { rd, rs1, imm }  => fmtLoad(out, a, "lb", rd, rs1, imm),
209 -
        case decode::Instr::Lh { rd, rs1, imm }  => fmtLoad(out, a, "lh", rd, rs1, imm),
210 -
        case decode::Instr::Lw { rd, rs1, imm }  => fmtLoad(out, a, "lw", rd, rs1, imm),
211 -
        case decode::Instr::Ld { rd, rs1, imm }  => fmtLoad(out, a, "ld", rd, rs1, imm),
212 -
        case decode::Instr::Lbu { rd, rs1, imm } => fmtLoad(out, a, "lbu", rd, rs1, imm),
213 -
        case decode::Instr::Lhu { rd, rs1, imm } => fmtLoad(out, a, "lhu", rd, rs1, imm),
214 -
        case decode::Instr::Lwu { rd, rs1, imm } => fmtLoad(out, a, "lwu", rd, rs1, imm),
215 -
        case decode::Instr::Sb { rs2, rs1, imm } => fmtStore(out, a, "sb", rs2, rs1, imm),
216 -
        case decode::Instr::Sh { rs2, rs1, imm } => fmtStore(out, a, "sh", rs2, rs1, imm),
217 -
        case decode::Instr::Sw { rs2, rs1, imm } => fmtStore(out, a, "sw", rs2, rs1, imm),
218 -
        case decode::Instr::Sd { rs2, rs1, imm } => fmtStore(out, a, "sd", rs2, rs1, imm),
208 +
        case decode::Instr::Blt { rs1, rs2, imm }  => fmtB(out, "blt", rs1, rs2, imm),
209 +
        case decode::Instr::Bge { rs1, rs2, imm }  => fmtB(out, "bge", rs1, rs2, imm),
210 +
        case decode::Instr::Bltu { rs1, rs2, imm } => fmtB(out, "bltu", rs1, rs2, imm),
211 +
        case decode::Instr::Bgeu { rs1, rs2, imm } => fmtB(out, "bgeu", rs1, rs2, imm),
212 +
        case decode::Instr::Lb { rd, rs1, imm }  => fmtLoad(out, "lb", rd, rs1, imm),
213 +
        case decode::Instr::Lh { rd, rs1, imm }  => fmtLoad(out, "lh", rd, rs1, imm),
214 +
        case decode::Instr::Lw { rd, rs1, imm }  => fmtLoad(out, "lw", rd, rs1, imm),
215 +
        case decode::Instr::Ld { rd, rs1, imm }  => fmtLoad(out, "ld", rd, rs1, imm),
216 +
        case decode::Instr::Lbu { rd, rs1, imm } => fmtLoad(out, "lbu", rd, rs1, imm),
217 +
        case decode::Instr::Lhu { rd, rs1, imm } => fmtLoad(out, "lhu", rd, rs1, imm),
218 +
        case decode::Instr::Lwu { rd, rs1, imm } => fmtLoad(out, "lwu", rd, rs1, imm),
219 +
        case decode::Instr::Sb { rs2, rs1, imm } => fmtStore(out, "sb", rs2, rs1, imm),
220 +
        case decode::Instr::Sh { rs2, rs1, imm } => fmtStore(out, "sh", rs2, rs1, imm),
221 +
        case decode::Instr::Sw { rs2, rs1, imm } => fmtStore(out, "sw", rs2, rs1, imm),
222 +
        case decode::Instr::Sd { rs2, rs1, imm } => fmtStore(out, "sd", rs2, rs1, imm),
219 223
        case decode::Instr::Addi { rd, rs1, imm } => {
220 224
            if *rd == 0 and *rs1 == 0 and imm == 0 {
221 225
                write(out, "nop");
222 226
            } else if imm == 0 {
223 227
                fmt2R(out, "mv", rd, rs1);
224 228
            } else if *rs1 == 0 {
225 -
                fmtRI(out, a, "li", rd, imm);
229 +
                fmtRI(out, "li", rd, imm);
226 230
            } else {
227 -
                fmtI(out, a, "addi", rd, rs1, imm);
231 +
                fmtI(out, "addi", rd, rs1, imm);
228 232
            }
229 233
        },
230 -
        case decode::Instr::Slti { rd, rs1, imm }  => fmtI(out, a, "slti", rd, rs1, imm),
234 +
        case decode::Instr::Slti { rd, rs1, imm }  => fmtI(out, "slti", rd, rs1, imm),
231 235
        case decode::Instr::Sltiu { rd, rs1, imm } => {
232 236
            if imm == 1 {
233 237
                fmt2R(out, "seqz", rd, rs1);
234 238
            } else {
235 -
                fmtI(out, a, "sltiu", rd, rs1, imm);
239 +
                fmtI(out, "sltiu", rd, rs1, imm);
236 240
            }
237 241
        },
238 242
        case decode::Instr::Xori { rd, rs1, imm } => {
239 243
            if imm == -1 {
240 244
                fmt2R(out, "not", rd, rs1);
241 245
            } else {
242 -
                fmtI(out, a, "xori", rd, rs1, imm);
246 +
                fmtI(out, "xori", rd, rs1, imm);
243 247
            }
244 248
        },
245 -
        case decode::Instr::Ori { rd, rs1, imm }  => fmtI(out, a, "ori", rd, rs1, imm),
246 -
        case decode::Instr::Andi { rd, rs1, imm } => fmtI(out, a, "andi", rd, rs1, imm),
247 -
        case decode::Instr::Slli { rd, rs1, shamt } => fmtI(out, a, "slli", rd, rs1, shamt),
248 -
        case decode::Instr::Srli { rd, rs1, shamt } => fmtI(out, a, "srli", rd, rs1, shamt),
249 -
        case decode::Instr::Srai { rd, rs1, shamt } => fmtI(out, a, "srai", rd, rs1, shamt),
249 +
        case decode::Instr::Ori { rd, rs1, imm }  => fmtI(out, "ori", rd, rs1, imm),
250 +
        case decode::Instr::Andi { rd, rs1, imm } => fmtI(out, "andi", rd, rs1, imm),
251 +
        case decode::Instr::Slli { rd, rs1, shamt } => fmtI(out, "slli", rd, rs1, shamt),
252 +
        case decode::Instr::Srli { rd, rs1, shamt } => fmtI(out, "srli", rd, rs1, shamt),
253 +
        case decode::Instr::Srai { rd, rs1, shamt } => fmtI(out, "srai", rd, rs1, shamt),
250 254
        case decode::Instr::Add { rd, rs1, rs2 } => fmtR(out, "add", rd, rs1, rs2),
251 255
        case decode::Instr::Sub { rd, rs1, rs2 } => {
252 256
            if *rs1 == 0 {
253 257
                fmt2R(out, "neg", rd, rs2);
254 258
            } else {
279 283
        case decode::Instr::Remu { rd, rs1, rs2 }   => fmtR(out, "remu", rd, rs1, rs2),
280 284
        case decode::Instr::Addiw { rd, rs1, imm } => {
281 285
            if imm == 0 {
282 286
                fmt2R(out, "sext.w", rd, rs1);
283 287
            } else {
284 -
                fmtI(out, a, "addiw", rd, rs1, imm);
288 +
                fmtI(out, "addiw", rd, rs1, imm);
285 289
            }
286 290
        },
287 -
        case decode::Instr::Slliw { rd, rs1, shamt } => fmtI(out, a, "slliw", rd, rs1, shamt),
288 -
        case decode::Instr::Srliw { rd, rs1, shamt } => fmtI(out, a, "srliw", rd, rs1, shamt),
289 -
        case decode::Instr::Sraiw { rd, rs1, shamt } => fmtI(out, a, "sraiw", rd, rs1, shamt),
291 +
        case decode::Instr::Slliw { rd, rs1, shamt } => fmtI(out, "slliw", rd, rs1, shamt),
292 +
        case decode::Instr::Srliw { rd, rs1, shamt } => fmtI(out, "srliw", rd, rs1, shamt),
293 +
        case decode::Instr::Sraiw { rd, rs1, shamt } => fmtI(out, "sraiw", rd, rs1, shamt),
290 294
        case decode::Instr::Addw { rd, rs1, rs2 } => fmtR(out, "addw", rd, rs1, rs2),
291 295
        case decode::Instr::Subw { rd, rs1, rs2 } => fmtR(out, "subw", rd, rs1, rs2),
292 296
        case decode::Instr::Sllw { rd, rs1, rs2 } => fmtR(out, "sllw", rd, rs1, rs2),
293 297
        case decode::Instr::Srlw { rd, rs1, rs2 } => fmtR(out, "srlw", rd, rs1, rs2),
294 298
        case decode::Instr::Sraw { rd, rs1, rs2 } => fmtR(out, "sraw", rd, rs1, rs2),
319 323
        case decode::Instr::FenceI => write(out, "fence.i"),
320 324
        case decode::Instr::Ecall  => write(out, "ecall"),
321 325
        case decode::Instr::Ebreak => write(out, "ebreak"),
322 326
        case decode::Instr::Unknown { bits } => {
323 327
            write(out, "unknown");
324 -
            writeParens(out, formatU32(a, bits));
328 +
            write(out, "(");
329 +
            writeU32(out, bits);
330 +
            write(out, ")");
325 331
        },
326 332
    }
327 333
}
328 334
329 335
/// Print code with labels to the given output.
330 -
export unsafe fn printCodeTo(out: &mut opaque sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: &mut alloc::Arena) {
336 +
export fn printCodeTo(out: &mut opaque sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr]) {
331 337
    // Package header.
332 338
    write(out, "# package `");
333 339
    write(out, pkgName);
334 340
    write(out, "`\n\n");
335 341
337 343
        if let name = findFunc(funcs, i) {
338 344
            write(out, "\n# ");
339 345
            write(out, name);
340 346
            write(out, "\n\n");
341 347
        }
342 -
        printInstr(out, arena, instr);
348 +
        printInstr(out, instr);
343 349
        write(out, "\n");
344 350
    }
345 351
}
346 352
347 353
/// Find function at given instruction index.