authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2022-10-10 00:09:18-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2022-10-25 05:11:28-04:00
logc126a1018eec54c568aaa642013e3c86bdd6d3e4
treec1b40bf061cf532ff5dcfe88dc93cd07cb56ddfb
parenta12535f5014cb2d4878581871287f17a31f28961

cbe: implement more asm features


2 files changed, 149 insertions(+), 80 deletions(-)

src/codegen/c.zig+149-79
...@@ -274,6 +274,13 @@ pub const Function = struct {...@@ -274,6 +274,13 @@ pub const Function = struct {
274 }274 }
275 }275 }
276276
277 fn wantSafety(f: *Function) bool {
278 return switch (f.object.dg.module.optimizeMode()) {
279 .Debug, .ReleaseSafe => true,
280 .ReleaseFast, .ReleaseSmall => false,
281 };
282 }
283
277 fn allocLocalValue(f: *Function) CValue {284 fn allocLocalValue(f: *Function) CValue {
278 const result = f.next_local_index;285 const result = f.next_local_index;
279 f.next_local_index += 1;286 f.next_local_index += 1;
...@@ -528,9 +535,7 @@ pub const DeclGen = struct {...@@ -528,9 +535,7 @@ pub const DeclGen = struct {
528 .Int,535 .Int,
529 .Enum,536 .Enum,
530 .ErrorSet,537 .ErrorSet,
531 => return writer.print("{x}", .{538 => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}),
532 try dg.fmtIntLiteral(ty, val, location),
533 }),
534 .Float => switch (ty.tag()) {539 .Float => switch (ty.tag()) {
535 .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{540 .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{
536 try dg.fmtIntLiteral(Type.u32, val, location),541 try dg.fmtIntLiteral(Type.u32, val, location),
...@@ -2619,16 +2624,13 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2619,16 +2624,13 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
2619}2624}
26202625
2621fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {2626fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
2622 switch (f.object.dg.module.optimizeMode()) {2627 if (f.wantSafety()) {
2623 .Debug, .ReleaseSafe => {2628 const writer = f.object.writer();
2624 const writer = f.object.writer();2629 try writer.writeAll("memset(");
2625 try writer.writeAll("memset(");2630 try f.writeCValue(writer, dest_ptr);
2626 try f.writeCValue(writer, dest_ptr);2631 try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)});
2627 try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)});2632 try f.writeCValueDeref(writer, dest_ptr);
2628 try f.writeCValueDeref(writer, dest_ptr);2633 try writer.writeAll("));\n");
2629 try writer.writeAll("));\n");
2630 },
2631 .ReleaseFast, .ReleaseSmall => {},
2632 }2634 }
2633 return CValue.none;2635 return CValue.none;
2634}2636}
...@@ -3463,29 +3465,47 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3463,29 +3465,47 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
34633465
3464 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;3466 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
34653467
3466 if (outputs.len > 1) {3468 const writer = f.object.writer();
3467 return f.fail("TODO implement codegen for asm with more than 1 output", .{});3469 const inst_ty = f.air.typeOfIndex(inst);
3468 }3470 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: {
34693471 const local = try f.allocLocal(inst_ty, .Mut);
3470 const output_constraint: ?[]const u8 = for (outputs) |output| {3472 if (f.wantSafety()) {
3471 if (output != .none) {3473 try writer.writeAll(" = ");
3472 return f.fail("TODO implement codegen for non-expr asm", .{});3474 try f.object.dg.renderValue(writer, inst_ty, Value.undef, .Other);
3473 }3475 }
3476 try writer.writeAll(";\n");
3477 break :local local;
3478 } else .none;
3479
3480 try writer.writeAll("{\n");
3481 f.object.indent_writer.pushIndent();
3482
3483 const constraints_extra_begin = extra_i;
3484 for (outputs) |output| {
3474 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);3485 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
3475 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);3486 const constraint = std.mem.sliceTo(extra_bytes, 0);
3476 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);3487 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
3477 // This equation accounts for the fact that even if we have exactly 4 bytes3488 // This equation accounts for the fact that even if we have exactly 4 bytes
3478 // for the string, we still use the next u32 for the null terminator.3489 // for the string, we still use the next u32 for the null terminator.
3479 extra_i += (constraint.len + name.len + (2 + 3)) / 4;3490 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
34803491
3481 break constraint;3492 const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType();
3482 } else null;3493 try writer.writeAll("register ");
34833494 try f.object.dg.renderTypeAndName(writer, output_ty, .{ .identifier = name }, .Mut, 0);
3484 const writer = f.object.writer();3495 if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) {
3485 try writer.writeAll("{\n");3496 try writer.writeAll(" __asm(\"");
34863497 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);
3487 const inputs_extra_begin = extra_i;3498 try writer.writeAll("\")");
3488 for (inputs) |input, i| {3499 } else if (constraint.len < 2 or constraint[0] != '=') {
3500 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
3501 }
3502 if (f.wantSafety()) {
3503 try writer.writeAll(" = ");
3504 try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other);
3505 }
3506 try writer.writeAll(";\n");
3507 }
3508 for (inputs) |input| {
3489 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);3509 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
3490 const constraint = std.mem.sliceTo(extra_bytes, 0);3510 const constraint = std.mem.sliceTo(extra_bytes, 0);
3491 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);3511 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
...@@ -3493,24 +3513,20 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3493,24 +3513,20 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
3493 // for the string, we still use the next u32 for the null terminator.3513 // for the string, we still use the next u32 for the null terminator.
3494 extra_i += (constraint.len + name.len + (2 + 3)) / 4;3514 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
34953515
3496 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {3516 const input_ty = f.air.typeOf(input);
3497 const reg = constraint[1 .. constraint.len - 1];3517 try writer.writeAll("register ");
3498 const arg_c_value = try f.resolveInst(input);3518 try f.object.dg.renderTypeAndName(writer, input_ty, .{ .identifier = name }, .Const, 0);
3499 try writer.writeAll("register ");3519 if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) {
3500 try f.renderType(writer, f.air.typeOf(input));3520 try writer.writeAll(" __asm(\"");
35013521 try writer.writeAll(constraint["{".len .. constraint.len - "}".len]);
3502 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });3522 try writer.writeAll("\")");
3503 try f.writeCValue(writer, arg_c_value);3523 } else if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null) {
3504 try writer.writeAll(";\n");3524 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
3505 } else {
3506 try writer.writeAll("register ");
3507 try f.renderType(writer, f.air.typeOf(input));
3508 try writer.print(" input_{d} = ", .{i});
3509 try f.writeCValue(writer, try f.resolveInst(input));
3510 try writer.writeAll(";\n");
3511 }3525 }
3526 try writer.writeAll(" = ");
3527 try f.writeCValue(writer, try f.resolveInst(input));
3528 try writer.writeAll(";\n");
3512 }3529 }
3513
3514 {3530 {
3515 var clobber_i: u32 = 0;3531 var clobber_i: u32 = 0;
3516 while (clobber_i < clobbers_len) : (clobber_i += 1) {3532 while (clobber_i < clobbers_len) : (clobber_i += 1) {
...@@ -3518,53 +3534,79 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3518,53 +3534,79 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
3518 // This equation accounts for the fact that even if we have exactly 4 bytes3534 // This equation accounts for the fact that even if we have exactly 4 bytes
3519 // for the string, we still use the next u32 for the null terminator.3535 // for the string, we still use the next u32 for the null terminator.
3520 extra_i += clobber.len / 4 + 1;3536 extra_i += clobber.len / 4 + 1;
3521
3522 // TODO honor these
3523 }3537 }
3524 }3538 }
3525
3526 const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];3539 const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];
35273540
3528 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";3541 try writer.writeAll("__asm");
3529 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });3542 if (is_volatile) try writer.writeAll(" volatile");
3530 if (output_constraint) |_| {3543 try writer.print("({s}", .{fmtStringLiteral(asm_source)});
3531 return f.fail("TODO: CBE inline asm output", .{});3544
3545 extra_i = constraints_extra_begin;
3546 try writer.writeByte(':');
3547 for (outputs) |_, index| {
3548 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
3549 const constraint = std.mem.sliceTo(extra_bytes, 0);
3550 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
3551 // This equation accounts for the fact that even if we have exactly 4 bytes
3552 // for the string, we still use the next u32 for the null terminator.
3553 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
3554
3555 if (index > 0) try writer.writeByte(',');
3556 try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[1] == '{') "=r" else constraint)});
3557 try f.writeCValue(writer, .{ .identifier = name });
3558 try writer.writeByte(')');
3532 }3559 }
3533 if (inputs.len > 0) {3560 try writer.writeByte(':');
3534 if (output_constraint == null) {3561 for (inputs) |_, index| {
3535 try writer.writeAll(" :");3562 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
3536 }3563 const constraint = std.mem.sliceTo(extra_bytes, 0);
3537 try writer.writeAll(": ");3564 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
3538 extra_i = inputs_extra_begin;3565 // This equation accounts for the fact that even if we have exactly 4 bytes
3539 for (inputs) |_, index| {3566 // for the string, we still use the next u32 for the null terminator.
3540 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);3567 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
3541 const constraint = std.mem.sliceTo(extra_bytes, 0);3568
3542 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);3569 if (index > 0) try writer.writeByte(',');
3570 try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[0] == '{') "r" else constraint)});
3571 try f.writeCValue(writer, .{ .identifier = name });
3572 try writer.writeByte(')');
3573 }
3574 try writer.writeByte(':');
3575 {
3576 var clobber_i: u32 = 0;
3577 while (clobber_i < clobbers_len) : (clobber_i += 1) {
3578 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
3543 // This equation accounts for the fact that even if we have exactly 4 bytes3579 // This equation accounts for the fact that even if we have exactly 4 bytes
3544 // for the string, we still use the next u32 for the null terminator.3580 // for the string, we still use the next u32 for the null terminator.
3545 extra_i += (constraint.len + name.len + (2 + 3)) / 4;3581 extra_i += clobber.len / 4 + 1;
35463582
3547 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {3583 if (clobber.len == 0) continue;
3548 const reg = constraint[1 .. constraint.len - 1];3584
3549 if (index > 0) {3585 if (clobber_i > 0) try writer.writeByte(',');
3550 try writer.writeAll(", ");3586 try writer.print(" {s}", .{fmtStringLiteral(clobber)});
3551 }
3552 try writer.print("\"r\"({s}_constant)", .{reg});
3553 } else {
3554 if (index > 0) {
3555 try writer.writeAll(", ");
3556 }
3557 try writer.print("\"r\"(input_{d})", .{index});
3558 }
3559 }3587 }
3560 }3588 }
3561 try writer.writeAll(");\n");3589 try writer.writeAll(");\n");
3562 try writer.writeAll("}\n");
35633590
3564 if (f.liveness.isUnused(inst))3591 extra_i = constraints_extra_begin;
3565 return CValue.none;3592 for (outputs) |output| {
3593 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
3594 const constraint = std.mem.sliceTo(extra_bytes, 0);
3595 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
3596 // This equation accounts for the fact that even if we have exactly 4 bytes
3597 // for the string, we still use the next u32 for the null terminator.
3598 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
3599
3600 try f.writeCValueDeref(writer, if (output == .none) .{ .local_ref = local.local } else try f.resolveInst(output));
3601 try writer.writeAll(" = ");
3602 try f.writeCValue(writer, .{ .identifier = name });
3603 try writer.writeAll(";\n");
3604 }
35663605
3567 return f.fail("TODO: C backend: inline asm expression result used", .{});3606 f.object.indent_writer.popIndent();
3607 try writer.writeAll("}\n");
3608
3609 return local;
3568}3610}
35693611
3570fn airIsNull(3612fn airIsNull(
...@@ -4634,6 +4676,34 @@ fn signAbbrev(signedness: std.builtin.Signedness) u8 {...@@ -4634,6 +4676,34 @@ fn signAbbrev(signedness: std.builtin.Signedness) u8 {
4634 };4676 };
4635}4677}
46364678
4679fn formatStringLiteral(
4680 str: []const u8,
4681 comptime fmt: []const u8,
4682 _: std.fmt.FormatOptions,
4683 writer: anytype,
4684) @TypeOf(writer).Error!void {
4685 if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt);
4686 try writer.writeByte('\"');
4687 for (str) |c| switch (c) {
4688 7 => try writer.writeAll("\\a"),
4689 8 => try writer.writeAll("\\b"),
4690 '\t' => try writer.writeAll("\\t"),
4691 '\n' => try writer.writeAll("\\n"),
4692 11 => try writer.writeAll("\\v"),
4693 12 => try writer.writeAll("\\f"),
4694 '\r' => try writer.writeAll("\\r"),
4695 '"', '\'', '?', '\\' => try writer.print("\\{c}", .{c}),
4696 else => switch (c) {
4697 ' '...'~' => try writer.writeByte(c),
4698 else => try writer.print("\\{o:0>3}", .{c}),
4699 },
4700 };
4701 try writer.writeByte('\"');
4702}
4703fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) {
4704 return .{ .data = str };
4705}
4706
4637const FormatIntLiteralContext = struct {4707const FormatIntLiteralContext = struct {
4638 ty: Type,4708 ty: Type,
4639 val: Value,4709 val: Value,
test/behavior/asm.zig-1
...@@ -30,7 +30,6 @@ test "module level assembly" {...@@ -30,7 +30,6 @@ test "module level assembly" {
30}30}
3131
32test "output constraint modifiers" {32test "output constraint modifiers" {
33 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
34 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO33 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
35 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO34 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
36 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO35 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO