authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-04 19:49:18-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-04 19:49:18-04:00
logda596b7e4febc95ec5249c9f489166944fbe69b9
tree7aa66aea2c4c0894348c2cb50729c4ddecb735d9
parent6aa668e0206da8b9233bfb90a2ffe1f692bf18bd
parentfb58fb2d8dfa49b01594f341f262cb45958c5e23
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6255 from joachimschmidt557/stage2-arm

stage2 ARM: more instructions, return values, parameters

4 files changed, 642 insertions(+), 140 deletions(-)

src/codegen.zig+154-16
......@@ -570,6 +570,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
570570 try self.dbgSetEpilogueBegin();
571571 }
572572 },
573 .arm => {
574 const cc = self.fn_type.fnCallingConvention();
575 if (cc != .Naked) {
576 // push {fp, lr}
577 // mov fp, sp
578 // sub sp, sp, #reloc
579 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32());
580 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32());
581 // TODO: prepare stack for local variables
582 // const backpatch_reloc = try self.code.addManyAsArray(4);
583
584 try self.dbgSetPrologueEnd();
585
586 try self.genBody(self.mod_fn.analysis.success);
587
588 // Backpatch stack offset
589 // const stack_end = self.max_end_stack;
590 // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
591 // mem.writeIntLittle(u32, backpatch_reloc, Instruction.sub(.al, .sp, .sp, Instruction.Operand.imm()));
592
593 try self.dbgSetEpilogueBegin();
594
595 // mov sp, fp
596 // pop {fp, pc}
597 // TODO: return by jumping to this code, use relocations
598 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
599 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
600 } else {
601 try self.dbgSetPrologueEnd();
602 try self.genBody(self.mod_fn.analysis.success);
603 try self.dbgSetEpilogueBegin();
604 }
605 },
573606 else => {
574607 try self.dbgSetPrologueEnd();
575608 try self.genBody(self.mod_fn.analysis.success);
......@@ -1461,7 +1494,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14611494 }
14621495 },
14631496 .arm => {
1464 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1497 for (info.args) |mc_arg, arg_i| {
1498 const arg = inst.args[arg_i];
1499 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1500
1501 switch (mc_arg) {
1502 .none => continue,
1503 .undef => unreachable,
1504 .immediate => unreachable,
1505 .unreach => unreachable,
1506 .dead => unreachable,
1507 .embedded_in_code => unreachable,
1508 .memory => unreachable,
1509 .compare_flags_signed => unreachable,
1510 .compare_flags_unsigned => unreachable,
1511 .register => |reg| {
1512 try self.genSetReg(arg.src, reg, arg_mcv);
1513 // TODO interact with the register allocator to mark the instruction as moved.
1514 },
1515 .stack_offset => {
1516 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1517 },
1518 .ptr_stack_offset => {
1519 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1520 },
1521 .ptr_embedded_in_code => {
1522 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1523 },
1524 }
1525 }
14651526
14661527 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
14671528 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
......@@ -1476,13 +1537,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14761537 else
14771538 unreachable;
14781539
1479 // TODO only works with leaf functions
1480 // at the moment, which works fine for
1481 // Hello World, but not for real code
1482 // of course. Add pushing lr to stack
1483 // and popping after call
14841540 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
1485 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1541
1542 // TODO: add Instruction.supportedOn
1543 // function for ARM
1544 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
1545 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1546 } else {
1547 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32());
1548 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1549 }
14861550 } else {
14871551 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
14881552 }
......@@ -1602,7 +1666,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16021666 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
16031667 },
16041668 .arm => {
1605 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1669 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
1670 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
1671 // TODO: jump to the end with relocation
1672 // // Just add space for an instruction, patch this later
1673 // try self.code.resize(self.code.items.len + 4);
1674 // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
16061675 },
16071676 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
16081677 }
......@@ -2214,14 +2283,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22142283 // least amount of necessary instructions (use
22152284 // more intelligent rotating)
22162285 if (x <= math.maxInt(u8)) {
2217 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2286 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
22182287 return;
22192288 } else if (x <= math.maxInt(u16)) {
22202289 // TODO Use movw Note: Not supported on
22212290 // all ARM targets!
22222291
2223 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2224 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2292 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2293 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
22252294 } else if (x <= math.maxInt(u32)) {
22262295 // TODO Use movw and movt Note: Not
22272296 // supported on all ARM targets! Also TODO
......@@ -2233,20 +2302,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22332302 // orr reg, reg, #0xbb, 24
22342303 // orr reg, reg, #0xcc, 16
22352304 // orr reg, reg, #0xdd, 8
2236 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2237 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2238 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2239 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2305 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2306 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2307 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2308 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
22402309 return;
22412310 } else {
22422311 return self.fail(src, "ARM registers are 32-bit wide", .{});
22432312 }
22442313 },
2314 .register => |src_reg| {
2315 // If the registers are the same, nothing to do.
2316 if (src_reg.id() == reg.id())
2317 return;
2318
2319 // mov reg, src_reg
2320 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32());
2321 },
22452322 .memory => |addr| {
22462323 // The value is in memory at a hard-coded address.
22472324 // If the type is a pointer, it means the pointer address is at this memory location.
22482325 try self.genSetReg(src, reg, .{ .immediate = addr });
2249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
2326 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
22502327 },
22512328 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
22522329 },
......@@ -2702,6 +2779,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27022779 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
27032780 }
27042781 },
2782 .arm => {
2783 switch (cc) {
2784 .Naked => {
2785 assert(result.args.len == 0);
2786 result.return_value = .{ .unreach = {} };
2787 result.stack_byte_count = 0;
2788 result.stack_align = 1;
2789 return result;
2790 },
2791 .Unspecified, .C => {
2792 // ARM Procedure Call Standard, Chapter 6.5
2793 var ncrn: usize = 0; // Next Core Register Number
2794 var nsaa: u32 = 0; // Next stacked argument address
2795
2796 for (param_types) |ty, i| {
2797 if (ty.abiAlignment(self.target.*) == 8) {
2798 // Round up NCRN to the next even number
2799 ncrn += ncrn % 2;
2800 }
2801
2802 const param_size = @intCast(u32, ty.abiSize(self.target.*));
2803 if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
2804 if (param_size <= 4) {
2805 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
2806 ncrn += 1;
2807 } else {
2808 return self.fail(src, "TODO MCValues with multiple registers", .{});
2809 }
2810 } else if (ncrn < 4 and nsaa == 0) {
2811 return self.fail(src, "TODO MCValues split between registers and stack", .{});
2812 } else {
2813 ncrn = 4;
2814 if (ty.abiAlignment(self.target.*) == 8) {
2815 if (nsaa % 8 != 0) {
2816 nsaa += 8 - (nsaa % 8);
2817 }
2818 }
2819
2820 result.args[i] = .{ .stack_offset = nsaa };
2821 nsaa += param_size;
2822 }
2823 }
2824
2825 result.stack_byte_count = nsaa;
2826 result.stack_align = 4;
2827 },
2828 else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),
2829 }
2830 },
27052831 else => if (param_types.len != 0)
27062832 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
27072833 }
......@@ -2720,6 +2846,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27202846 },
27212847 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
27222848 },
2849 .arm => switch (cc) {
2850 .Naked => unreachable,
2851 .Unspecified, .C => {
2852 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2853 if (ret_ty_size <= 4) {
2854 result.return_value = .{ .register = c_abi_int_return_regs[0] };
2855 } else {
2856 return self.fail(src, "TODO support more return types for ARM backend", .{});
2857 }
2858 },
2859 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
2860 },
27232861 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
27242862 }
27252863 return result;
src/codegen/arm.zig+342-55
......@@ -113,6 +113,13 @@ test "Register.id" {
113113 testing.expectEqual(@as(u4, 15), Register.pc.id());
114114}
115115
116/// Program status registers containing flags, mode bits and other
117/// vital information
118pub const Psr = enum {
119 cpsr,
120 spsr,
121};
122
116123pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117124pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118125pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
......@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {
135142 offset: u12,
136143 rd: u4,
137144 rn: u4,
138 l: u1,
139 w: u1,
140 b: u1,
141 u: u1,
142 p: u1,
143 i: u1,
145 load_store: u1,
146 write_back: u1,
147 byte_word: u1,
148 up_down: u1,
149 pre_post: u1,
150 imm: u1,
144151 fixed: u2 = 0b01,
145152 cond: u4,
146153 },
154 BlockDataTransfer: packed struct {
155 register_list: u16,
156 rn: u4,
157 load_store: u1,
158 write_back: u1,
159 psr_or_user: u1,
160 up_down: u1,
161 pre_post: u1,
162 fixed: u3 = 0b100,
163 cond: u4,
164 },
147165 Branch: packed struct {
148166 offset: u24,
149167 link: u1,
......@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {
235253 rs: u4,
236254 },
237255
238 const Type = enum(u2) {
239 LogicalLeft,
240 LogicalRight,
241 ArithmeticRight,
242 RotateRight,
256 pub const Type = enum(u2) {
257 logical_left,
258 logical_right,
259 arithmetic_right,
260 rotate_right,
243261 };
244262
245 const none = Shift{
263 pub const none = Shift{
246264 .Immediate = .{
247265 .amount = 0,
248266 .typ = 0,
......@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {
338356 }
339357 };
340358
359 /// Represents the register list operand to a block data transfer
360 /// instruction
361 pub const RegisterList = packed struct {
362 r0: bool = false,
363 r1: bool = false,
364 r2: bool = false,
365 r3: bool = false,
366 r4: bool = false,
367 r5: bool = false,
368 r6: bool = false,
369 r7: bool = false,
370 r8: bool = false,
371 r9: bool = false,
372 r10: bool = false,
373 r11: bool = false,
374 r12: bool = false,
375 r13: bool = false,
376 r14: bool = false,
377 r15: bool = false,
378 };
379
341380 pub fn toU32(self: Instruction) u32 {
342381 return switch (self) {
343382 .DataProcessing => |v| @bitCast(u32, v),
344383 .SingleDataTransfer => |v| @bitCast(u32, v),
384 .BlockDataTransfer => |v| @bitCast(u32, v),
345385 .Branch => |v| @bitCast(u32, v),
346386 .BranchExchange => |v| @bitCast(u32, v),
347387 .SupervisorCall => |v| @bitCast(u32, v),
......@@ -362,7 +402,7 @@ pub const Instruction = union(enum) {
362402 return Instruction{
363403 .DataProcessing = .{
364404 .cond = @enumToInt(cond),
365 .i = if (op2 == .Immediate) 1 else 0,
405 .i = @boolToInt(op2 == .Immediate),
366406 .opcode = @enumToInt(opcode),
367407 .s = s,
368408 .rn = rn.id(),
......@@ -377,10 +417,10 @@ pub const Instruction = union(enum) {
377417 rd: Register,
378418 rn: Register,
379419 offset: Offset,
380 pre_post: u1,
381 up_down: u1,
420 pre_index: bool,
421 positive: bool,
382422 byte_word: u1,
383 writeback: u1,
423 write_back: bool,
384424 load_store: u1,
385425 ) Instruction {
386426 return Instruction{
......@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {
389429 .rn = rn.id(),
390430 .rd = rd.id(),
391431 .offset = offset.toU12(),
392 .l = load_store,
393 .w = writeback,
394 .b = byte_word,
395 .u = up_down,
396 .p = pre_post,
397 .i = if (offset == .Immediate) 0 else 1,
432 .load_store = load_store,
433 .write_back = @boolToInt(write_back),
434 .byte_word = byte_word,
435 .up_down = @boolToInt(positive),
436 .pre_post = @boolToInt(pre_index),
437 .imm = @boolToInt(offset != .Immediate),
438 },
439 };
440 }
441
442 fn blockDataTransfer(
443 cond: Condition,
444 rn: Register,
445 reg_list: RegisterList,
446 pre_post: u1,
447 up_down: u1,
448 psr_or_user: u1,
449 write_back: bool,
450 load_store: u1,
451 ) Instruction {
452 return Instruction{
453 .BlockDataTransfer = .{
454 .register_list = @bitCast(u16, reg_list),
455 .rn = rn.id(),
456 .load_store = load_store,
457 .write_back = @boolToInt(write_back),
458 .psr_or_user = psr_or_user,
459 .up_down = up_down,
460 .pre_post = pre_post,
461 .cond = @enumToInt(cond),
398462 },
399463 };
400464 }
......@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {
442506
443507 // Data processing
444508
445 pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
446 return dataProcessing(cond, .@"and", s, rd, rn, op2);
509 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
510 return dataProcessing(cond, .@"and", 0, rd, rn, op2);
511 }
512
513 pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
514 return dataProcessing(cond, .@"and", 1, rd, rn, op2);
515 }
516
517 pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
518 return dataProcessing(cond, .eor, 0, rd, rn, op2);
519 }
520
521 pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
522 return dataProcessing(cond, .eor, 1, rd, rn, op2);
523 }
524
525 pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
526 return dataProcessing(cond, .sub, 0, rd, rn, op2);
527 }
528
529 pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
530 return dataProcessing(cond, .sub, 1, rd, rn, op2);
531 }
532
533 pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
534 return dataProcessing(cond, .rsb, 0, rd, rn, op2);
535 }
536
537 pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
538 return dataProcessing(cond, .rsb, 1, rd, rn, op2);
539 }
540
541 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
542 return dataProcessing(cond, .add, 0, rd, rn, op2);
447543 }
448544
449 pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
450 return dataProcessing(cond, .eor, s, rd, rn, op2);
545 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
546 return dataProcessing(cond, .add, 1, rd, rn, op2);
451547 }
452548
453 pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
454 return dataProcessing(cond, .sub, s, rd, rn, op2);
549 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
550 return dataProcessing(cond, .adc, 0, rd, rn, op2);
455551 }
456552
457 pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
458 return dataProcessing(cond, .rsb, s, rd, rn, op2);
553 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
554 return dataProcessing(cond, .adc, 1, rd, rn, op2);
459555 }
460556
461 pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
462 return dataProcessing(cond, .add, s, rd, rn, op2);
557 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
558 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
463559 }
464560
465 pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
466 return dataProcessing(cond, .adc, s, rd, rn, op2);
561 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
562 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
467563 }
468564
469 pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
470 return dataProcessing(cond, .sbc, s, rd, rn, op2);
565 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
566 return dataProcessing(cond, .rsc, 0, rd, rn, op2);
471567 }
472568
473 pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
474 return dataProcessing(cond, .rsc, s, rd, rn, op2);
569 pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
570 return dataProcessing(cond, .rsc, 1, rd, rn, op2);
475571 }
476572
477573 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
......@@ -490,32 +586,115 @@ pub const Instruction = union(enum) {
490586 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
491587 }
492588
493 pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
494 return dataProcessing(cond, .orr, s, rd, rn, op2);
589 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
590 return dataProcessing(cond, .orr, 0, rd, rn, op2);
591 }
592
593 pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
594 return dataProcessing(cond, .orr, 1, rd, rn, op2);
595 }
596
597 pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
598 return dataProcessing(cond, .mov, 0, rd, .r0, op2);
495599 }
496600
497 pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
498 return dataProcessing(cond, .mov, s, rd, .r0, op2);
601 pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
602 return dataProcessing(cond, .mov, 1, rd, .r0, op2);
499603 }
500604
501 pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
502 return dataProcessing(cond, .bic, s, rd, rn, op2);
605 pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction {
606 return dataProcessing(cond, .bic, 0, rd, rn, op2);
503607 }
504608
505 pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
506 return dataProcessing(cond, .mvn, s, rd, .r0, op2);
609 pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction {
610 return dataProcessing(cond, .bic, 1, rd, rn, op2);
611 }
612
613 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
614 return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
615 }
616
617 pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
618 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
619 }
620
621 // PSR transfer
622
623 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
624 return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none));
507625 }
508626
509627 // Single data transfer
510628
511 pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
512 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
629 pub const OffsetArgs = struct {
630 pre_index: bool = true,
631 positive: bool = true,
632 offset: Offset,
633 write_back: bool = false,
634 };
635
636 pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
637 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
638 }
639
640 pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
641 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
642 }
643
644 pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
645 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
646 }
647
648 pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
649 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
650 }
651
652 // Block data transfer
653
654 pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
655 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
656 }
657
658 pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
659 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
513660 }
514661
515 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
516 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
662 pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
663 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
517664 }
518665
666 pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
667 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
668 }
669
670 pub const ldmfa = ldmda;
671 pub const ldmea = ldmdb;
672 pub const ldmed = ldmib;
673 pub const ldmfd = ldmia;
674 pub const ldm = ldmia;
675
676 pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
677 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
678 }
679
680 pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
681 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
682 }
683
684 pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
685 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
686 }
687
688 pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
689 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
690 }
691
692 pub const stmed = stmda;
693 pub const stmfd = stmdb;
694 pub const stmfa = stmib;
695 pub const stmea = stmia;
696 pub const stm = stmia;
697
519698 // Branch
520699
521700 pub fn b(cond: Condition, offset: i24) Instruction {
......@@ -549,6 +728,58 @@ pub const Instruction = union(enum) {
549728 pub fn bkpt(imm: u16) Instruction {
550729 return breakpoint(imm);
551730 }
731
732 // Aliases
733
734 pub fn pop(cond: Condition, args: anytype) Instruction {
735 if (@typeInfo(@TypeOf(args)) != .Struct) {
736 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
737 }
738
739 if (args.len < 1) {
740 @compileError("Expected at least one register");
741 } else if (args.len == 1) {
742 const reg = args[0];
743 return ldr(cond, reg, .sp, .{
744 .pre_index = false,
745 .positive = true,
746 .offset = Offset.imm(4),
747 .write_back = false,
748 });
749 } else {
750 var register_list: u16 = 0;
751 inline for (args) |arg| {
752 const reg = @as(Register, arg);
753 register_list |= @as(u16, 1) << reg.id();
754 }
755 return ldm(cond, .sp, true, @bitCast(RegisterList, register_list));
756 }
757 }
758
759 pub fn push(cond: Condition, args: anytype) Instruction {
760 if (@typeInfo(@TypeOf(args)) != .Struct) {
761 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
762 }
763
764 if (args.len < 1) {
765 @compileError("Expected at least one register");
766 } else if (args.len == 1) {
767 const reg = args[0];
768 return str(cond, reg, .sp, .{
769 .pre_index = true,
770 .positive = false,
771 .offset = Offset.imm(4),
772 .write_back = true,
773 });
774 } else {
775 var register_list: u16 = 0;
776 inline for (args) |arg| {
777 const reg = @as(Register, arg);
778 register_list |= @as(u16, 1) << reg.id();
779 }
780 return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
781 }
782 }
552783};
553784
554785test "serialize instructions" {
......@@ -559,23 +790,31 @@ test "serialize instructions" {
559790
560791 const testcases = [_]Testcase{
561792 .{ // add r0, r0, r0
562 .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
793 .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
563794 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
564795 },
565796 .{ // mov r4, r2
566 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
797 .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
567798 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
568799 },
569800 .{ // mov r0, #42
570 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
801 .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
571802 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
572803 },
804 .{ // mrs r5, cpsr
805 .inst = Instruction.mrs(.al, .r5, .cpsr),
806 .expected = 0b1110_00010_0_001111_0101_000000000000,
807 },
573808 .{ // ldr r0, [r2, #42]
574 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
809 .inst = Instruction.ldr(.al, .r0, .r2, .{
810 .offset = Instruction.Offset.imm(42),
811 }),
575812 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
576813 },
577814 .{ // str r0, [r3]
578 .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
815 .inst = Instruction.str(.al, .r0, .r3, .{
816 .offset = Instruction.Offset.none,
817 }),
579818 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
580819 },
581820 .{ // b #12
......@@ -598,6 +837,14 @@ test "serialize instructions" {
598837 .inst = Instruction.bkpt(42),
599838 .expected = 0b1110_0001_0010_000000000010_0111_1010,
600839 },
840 .{ // stmdb r9, {r0}
841 .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
842 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
843 },
844 .{ // ldmea r4!, {r2, r5}
845 .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
846 .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
847 },
601848 };
602849
603850 for (testcases) |case| {
......@@ -605,3 +852,43 @@ test "serialize instructions" {
605852 testing.expectEqual(case.expected, actual);
606853 }
607854}
855
856test "aliases" {
857 const Testcase = struct {
858 expected: Instruction,
859 actual: Instruction,
860 };
861
862 const testcases = [_]Testcase{
863 .{ // pop { r6 }
864 .actual = Instruction.pop(.al, .{.r6}),
865 .expected = Instruction.ldr(.al, .r6, .sp, .{
866 .pre_index = false,
867 .positive = true,
868 .offset = Instruction.Offset.imm(4),
869 .write_back = false,
870 }),
871 },
872 .{ // pop { r1, r5 }
873 .actual = Instruction.pop(.al, .{ .r1, .r5 }),
874 .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
875 },
876 .{ // push { r3 }
877 .actual = Instruction.push(.al, .{.r3}),
878 .expected = Instruction.str(.al, .r3, .sp, .{
879 .pre_index = true,
880 .positive = false,
881 .offset = Instruction.Offset.imm(4),
882 .write_back = true,
883 }),
884 },
885 .{ // push { r0, r2 }
886 .actual = Instruction.push(.al, .{ .r0, .r2 }),
887 .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
888 },
889 };
890
891 for (testcases) |case| {
892 testing.expectEqual(case.expected.toU32(), case.actual.toU32());
893 }
894}
test/stage2/arm.zig created+116
......@@ -0,0 +1,116 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const linux_arm = std.zig.CrossTarget{
5 .cpu_arch = .arm,
6 .os_tag = .linux,
7};
8
9pub fn addCases(ctx: *TestContext) !void {
10 {
11 var case = ctx.exe("hello world", linux_arm);
12 // Regular old hello world
13 case.addCompareOutput(
14 \\export fn _start() noreturn {
15 \\ print();
16 \\ exit();
17 \\}
18 \\
19 \\fn print() void {
20 \\ asm volatile ("svc #0"
21 \\ :
22 \\ : [number] "{r7}" (4),
23 \\ [arg1] "{r0}" (1),
24 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
25 \\ [arg3] "{r2}" (14)
26 \\ : "memory"
27 \\ );
28 \\ return;
29 \\}
30 \\
31 \\fn exit() noreturn {
32 \\ asm volatile ("svc #0"
33 \\ :
34 \\ : [number] "{r7}" (1),
35 \\ [arg1] "{r0}" (0)
36 \\ : "memory"
37 \\ );
38 \\ unreachable;
39 \\}
40 ,
41 "Hello, World!\n",
42 );
43 }
44
45 {
46 var case = ctx.exe("parameters and return values", linux_arm);
47 // Testing simple parameters and return values
48 //
49 // TODO: The parameters to the asm statement in print() had to
50 // be in a specific order because otherwise the write to r0
51 // would overwrite the len parameter which resides in r0
52 case.addCompareOutput(
53 \\export fn _start() noreturn {
54 \\ print(id(14));
55 \\ exit();
56 \\}
57 \\
58 \\fn id(x: u32) u32 {
59 \\ return x;
60 \\}
61 \\
62 \\fn print(len: u32) void {
63 \\ asm volatile ("svc #0"
64 \\ :
65 \\ : [number] "{r7}" (4),
66 \\ [arg3] "{r2}" (len),
67 \\ [arg1] "{r0}" (1),
68 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n"))
69 \\ : "memory"
70 \\ );
71 \\ return;
72 \\}
73 \\
74 \\fn exit() noreturn {
75 \\ asm volatile ("svc #0"
76 \\ :
77 \\ : [number] "{r7}" (1),
78 \\ [arg1] "{r0}" (0)
79 \\ : "memory"
80 \\ );
81 \\ unreachable;
82 \\}
83 ,
84 "Hello, World!\n",
85 );
86 }
87
88 {
89 var case = ctx.exe("non-leaf functions", linux_arm);
90 // Testing non-leaf functions
91 case.addCompareOutput(
92 \\export fn _start() noreturn {
93 \\ foo();
94 \\ exit();
95 \\}
96 \\
97 \\fn foo() void {
98 \\ bar();
99 \\}
100 \\
101 \\fn bar() void {}
102 \\
103 \\fn exit() noreturn {
104 \\ asm volatile ("svc #0"
105 \\ :
106 \\ : [number] "{r7}" (1),
107 \\ [arg1] "{r0}" (0)
108 \\ : "memory"
109 \\ );
110 \\ unreachable;
111 \\}
112 ,
113 "",
114 );
115 }
116}
test/stage2/test.zig+30-69
......@@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{
2121 .os_tag = .linux,
2222};
2323
24const linux_arm = std.zig.CrossTarget{
25 .cpu_arch = .arm,
26 .os_tag = .linux,
27};
28
2924const wasi = std.zig.CrossTarget{
3025 .cpu_arch = .wasm32,
3126 .os_tag = .wasi,
......@@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {
3530 try @import("zir.zig").addCases(ctx);
3631 try @import("cbe.zig").addCases(ctx);
3732 try @import("spu-ii.zig").addCases(ctx);
33 try @import("arm.zig").addCases(ctx);
3834
3935 {
4036 var case = ctx.exe("hello world with updates", linux_x64);
......@@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void {
7672 \\ );
7773 \\ unreachable;
7874 \\}
79 ,
75 ,
8076 "Hello, World!\n",
8177 );
8278 // Now change the message only
......@@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void {
108104 \\ );
109105 \\ unreachable;
110106 \\}
111 ,
107 ,
112108 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
113109 );
114110 // Now we print it twice.
......@@ -223,42 +219,7 @@ pub fn addCases(ctx: *TestContext) !void {
223219 \\ );
224220 \\ unreachable;
225221 \\}
226 ,
227 "Hello, World!\n",
228 );
229 }
230
231 {
232 var case = ctx.exe("hello world", linux_arm);
233 // Regular old hello world
234 case.addCompareOutput(
235 \\export fn _start() noreturn {
236 \\ print();
237 \\ exit();
238 \\}
239 \\
240 \\fn print() void {
241 \\ asm volatile ("svc #0"
242 \\ :
243 \\ : [number] "{r7}" (4),
244 \\ [arg1] "{r0}" (1),
245 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
246 \\ [arg3] "{r2}" (14)
247 \\ : "memory"
248 \\ );
249 \\ return;
250 \\}
251 \\
252 \\fn exit() noreturn {
253 \\ asm volatile ("svc #0"
254 \\ :
255 \\ : [number] "{r7}" (1),
256 \\ [arg1] "{r0}" (0)
257 \\ : "memory"
258 \\ );
259 \\ unreachable;
260 \\}
261 ,
222 ,
262223 "Hello, World!\n",
263224 );
264225 }
......@@ -283,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void {
283244 \\ );
284245 \\ unreachable;
285246 \\}
286 ,
247 ,
287248 "Hello, World!\n",
288249 );
289250 }
......@@ -310,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void {
310271 \\ );
311272 \\ unreachable;
312273 \\}
313 ,
274 ,
314275 "",
315276 );
316277 }
......@@ -337,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void {
337298 \\ );
338299 \\ unreachable;
339300 \\}
340 ,
301 ,
341302 "",
342303 );
343304 }
......@@ -368,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void {
368329 \\ );
369330 \\ unreachable;
370331 \\}
371 ,
332 ,
372333 "",
373334 );
374335
......@@ -401,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void {
401362 \\ );
402363 \\ unreachable;
403364 \\}
404 ,
365 ,
405366 "",
406367 );
407368
......@@ -437,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void {
437398 \\ );
438399 \\ unreachable;
439400 \\}
440 ,
401 ,
441402 "",
442403 );
443404
......@@ -474,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void {
474435 \\ );
475436 \\ unreachable;
476437 \\}
477 ,
438 ,
478439 "",
479440 );
480441
......@@ -504,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void {
504465 \\ );
505466 \\ unreachable;
506467 \\}
507 ,
468 ,
508469 "",
509470 );
510471
......@@ -538,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void {
538499 \\ );
539500 \\ unreachable;
540501 \\}
541 ,
502 ,
542503 "",
543504 );
544505
......@@ -562,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void {
562523 \\ );
563524 \\ unreachable;
564525 \\}
565 ,
526 ,
566527 "",
567528 );
568529
......@@ -601,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void {
601562 \\ );
602563 \\ unreachable;
603564 \\}
604 ,
565 ,
605566 "hello\nhello\nhello\nhello\n",
606567 );
607568
......@@ -638,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void {
638599 \\ );
639600 \\ unreachable;
640601 \\}
641 ,
602 ,
642603 "",
643604 );
644605
......@@ -680,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void {
680641 \\ );
681642 \\ unreachable;
682643 \\}
683 ,
644 ,
684645 "",
685646 );
686647
......@@ -732,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void {
732693 \\ );
733694 \\ unreachable;
734695 \\}
735 ,
696 ,
736697 "",
737698 );
738699
......@@ -794,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void {
794755 \\ );
795756 \\ unreachable;
796757 \\}
797 ,
758 ,
798759 "",
799760 );
800761
......@@ -827,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void {
827788 \\ );
828789 \\ unreachable;
829790 \\}
830 ,
791 ,
831792 "",
832793 );
833794
......@@ -859,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void {
859820 \\ );
860821 \\ unreachable;
861822 \\}
862 ,
823 ,
863824 "",
864825 );
865826
......@@ -884,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void {
884845 \\ );
885846 \\ unreachable;
886847 \\}
887 ,
848 ,
888849 "",
889850 );
890851
......@@ -910,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void {
910871 \\ );
911872 \\ unreachable;
912873 \\}
913 ,
874 ,
914875 "",
915876 );
916877
......@@ -943,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void {
943904 \\ );
944905 \\ unreachable;
945906 \\}
946 ,
907 ,
947908 "hello\nhello\nhello\nhello\nhello\n",
948909 );
949910 }
......@@ -1000,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void {
1000961 \\ bar();
1001962 \\}
1002963 \\fn bar() void {}
1003 ,
964 ,
1004965 "42\n",
1005966 );
1006967
......@@ -1018,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void {
1018979 \\ bar();
1019980 \\}
1020981 \\fn bar() void {}
1021 ,
982 ,
1022983 "42\n",
1023984 );
1024985
......@@ -1034,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void {
1034995 \\ bar();
1035996 \\}
1036997 \\fn bar() void {}
1037 ,
1038 // This is what you get when you take the bits of the IEE-754
1039 // representation of 42.0 and reinterpret them as an unsigned
1040 // integer. Guess that's a bug in wasmtime.
998 ,
999 // This is what you get when you take the bits of the IEE-754
1000 // representation of 42.0 and reinterpret them as an unsigned
1001 // integer. Guess that's a bug in wasmtime.
10411002 "1109917696\n",
10421003 );
10431004 }