| ... | @@ -274,6 +274,13 @@ pub const Function = struct { | ... | @@ -274,6 +274,13 @@ pub const Function = struct { |
| 274 | } | 274 | } |
| 275 | } | 275 | } |
| 276 | | 276 | |
| | 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 | } |
| 2620 | | 2625 | |
| 2621 | fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { | 2626 | fn 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 { |
| 3463 | | 3465 | |
| 3464 | if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; | 3466 | if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; |
| 3465 | | 3467 | |
| 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: { |
| 3469 | | 3471 | 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 bytes | 3488 | // 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; |
| 3480 | | 3491 | |
| 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 "); |
| 3483 | | 3494 | 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(\""); |
| 3486 | | 3497 | 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; |
| 3495 | | 3515 | |
| 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(\""); |
| 3501 | | 3521 | 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 bytes | 3534 | // 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]; |
| 3527 | | 3540 | |
| 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 bytes | 3579 | // 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; |
| 3546 | | 3582 | |
| 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"); | | |
| 3563 | | 3590 | |
| 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 | } |
| 3566 | | 3605 | |
| 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 | } |
| 3569 | | 3611 | |
| 3570 | fn airIsNull( | 3612 | fn 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 | } |
| 4636 | | 4678 | |
| | 4679 | fn 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 | } |
| | 4703 | fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) { |
| | 4704 | return .{ .data = str }; |
| | 4705 | } |
| | 4706 | |
| 4637 | const FormatIntLiteralContext = struct { | 4707 | const FormatIntLiteralContext = struct { |
| 4638 | ty: Type, | 4708 | ty: Type, |
| 4639 | val: Value, | 4709 | val: Value, |