| ... | ... | @@ -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 | 284 | fn allocLocalValue(f: *Function) CValue { |
| 278 | 285 | const result = f.next_local_index; |
| 279 | 286 | f.next_local_index += 1; |
| ... | ... | @@ -528,9 +535,7 @@ pub const DeclGen = struct { |
| 528 | 535 | .Int, |
| 529 | 536 | .Enum, |
| 530 | 537 | .ErrorSet, |
| 531 | | => return writer.print("{x}", .{ |
| 532 | | try dg.fmtIntLiteral(ty, val, location), |
| 533 | | }), |
| 538 | => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}), |
| 534 | 539 | .Float => switch (ty.tag()) { |
| 535 | 540 | .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ |
| 536 | 541 | try dg.fmtIntLiteral(Type.u32, val, location), |
| ... | ... | @@ -2619,16 +2624,13 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2619 | 2624 | } |
| 2620 | 2625 | |
| 2621 | 2626 | fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { |
| 2622 | | switch (f.object.dg.module.optimizeMode()) { |
| 2623 | | .Debug, .ReleaseSafe => { |
| 2624 | | const writer = f.object.writer(); |
| 2625 | | try writer.writeAll("memset("); |
| 2626 | | try f.writeCValue(writer, dest_ptr); |
| 2627 | | try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); |
| 2628 | | try f.writeCValueDeref(writer, dest_ptr); |
| 2629 | | try writer.writeAll("));\n"); |
| 2630 | | }, |
| 2631 | | .ReleaseFast, .ReleaseSmall => {}, |
| 2627 | if (f.wantSafety()) { |
| 2628 | const writer = f.object.writer(); |
| 2629 | try writer.writeAll("memset("); |
| 2630 | try f.writeCValue(writer, dest_ptr); |
| 2631 | try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); |
| 2632 | try f.writeCValueDeref(writer, dest_ptr); |
| 2633 | try writer.writeAll("));\n"); |
| 2632 | 2634 | } |
| 2633 | 2635 | return CValue.none; |
| 2634 | 2636 | } |
| ... | ... | @@ -3463,29 +3465,47 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3463 | 3465 | |
| 3464 | 3466 | if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; |
| 3465 | 3467 | |
| 3466 | | if (outputs.len > 1) { |
| 3467 | | return f.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 3468 | | } |
| 3469 | | |
| 3470 | | const output_constraint: ?[]const u8 = for (outputs) |output| { |
| 3471 | | if (output != .none) { |
| 3472 | | return f.fail("TODO implement codegen for non-expr asm", .{}); |
| 3468 | const writer = f.object.writer(); |
| 3469 | const inst_ty = f.air.typeOfIndex(inst); |
| 3470 | const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: { |
| 3471 | const local = try f.allocLocal(inst_ty, .Mut); |
| 3472 | if (f.wantSafety()) { |
| 3473 | try writer.writeAll(" = "); |
| 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 | 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 | 3487 | const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 3477 | 3488 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 3478 | 3489 | // for the string, we still use the next u32 for the null terminator. |
| 3479 | 3490 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 3480 | 3491 | |
| 3481 | | break constraint; |
| 3482 | | } else null; |
| 3483 | | |
| 3484 | | const writer = f.object.writer(); |
| 3485 | | try writer.writeAll("{\n"); |
| 3486 | | |
| 3487 | | const inputs_extra_begin = extra_i; |
| 3488 | | for (inputs) |input, i| { |
| 3492 | const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); |
| 3493 | try writer.writeAll("register "); |
| 3494 | try f.object.dg.renderTypeAndName(writer, output_ty, .{ .identifier = name }, .Mut, 0); |
| 3495 | if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { |
| 3496 | try writer.writeAll(" __asm(\""); |
| 3497 | try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); |
| 3498 | try writer.writeAll("\")"); |
| 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 | 3509 | const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); |
| 3490 | 3510 | const constraint = std.mem.sliceTo(extra_bytes, 0); |
| 3491 | 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 | 3513 | // for the string, we still use the next u32 for the null terminator. |
| 3494 | 3514 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 3495 | 3515 | |
| 3496 | | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 3497 | | const reg = constraint[1 .. constraint.len - 1]; |
| 3498 | | const arg_c_value = try f.resolveInst(input); |
| 3499 | | try writer.writeAll("register "); |
| 3500 | | try f.renderType(writer, f.air.typeOf(input)); |
| 3501 | | |
| 3502 | | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); |
| 3503 | | try f.writeCValue(writer, arg_c_value); |
| 3504 | | try writer.writeAll(";\n"); |
| 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"); |
| 3516 | const input_ty = f.air.typeOf(input); |
| 3517 | try writer.writeAll("register "); |
| 3518 | try f.object.dg.renderTypeAndName(writer, input_ty, .{ .identifier = name }, .Const, 0); |
| 3519 | if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { |
| 3520 | try writer.writeAll(" __asm(\""); |
| 3521 | try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); |
| 3522 | try writer.writeAll("\")"); |
| 3523 | } else if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null) { |
| 3524 | return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); |
| 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 | 3531 | var clobber_i: u32 = 0; |
| 3516 | 3532 | while (clobber_i < clobbers_len) : (clobber_i += 1) { |
| ... | ... | @@ -3518,53 +3534,79 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3518 | 3534 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 3519 | 3535 | // for the string, we still use the next u32 for the null terminator. |
| 3520 | 3536 | extra_i += clobber.len / 4 + 1; |
| 3521 | | |
| 3522 | | // TODO honor these |
| 3523 | 3537 | } |
| 3524 | 3538 | } |
| 3525 | | |
| 3526 | 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 ""; |
| 3529 | | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); |
| 3530 | | if (output_constraint) |_| { |
| 3531 | | return f.fail("TODO: CBE inline asm output", .{}); |
| 3541 | try writer.writeAll("__asm"); |
| 3542 | if (is_volatile) try writer.writeAll(" volatile"); |
| 3543 | try writer.print("({s}", .{fmtStringLiteral(asm_source)}); |
| 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) { |
| 3534 | | if (output_constraint == null) { |
| 3535 | | try writer.writeAll(" :"); |
| 3536 | | } |
| 3537 | | try writer.writeAll(": "); |
| 3538 | | extra_i = inputs_extra_begin; |
| 3539 | | for (inputs) |_, index| { |
| 3540 | | const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); |
| 3541 | | const constraint = std.mem.sliceTo(extra_bytes, 0); |
| 3542 | | const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 3560 | try writer.writeByte(':'); |
| 3561 | for (inputs) |_, index| { |
| 3562 | const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); |
| 3563 | const constraint = std.mem.sliceTo(extra_bytes, 0); |
| 3564 | const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 3565 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 3566 | // for the string, we still use the next u32 for the null terminator. |
| 3567 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 3568 | |
| 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 | 3579 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 3544 | 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] == '}') { |
| 3548 | | const reg = constraint[1 .. constraint.len - 1]; |
| 3549 | | if (index > 0) { |
| 3550 | | try writer.writeAll(", "); |
| 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 | | } |
| 3583 | if (clobber.len == 0) continue; |
| 3584 | |
| 3585 | if (clobber_i > 0) try writer.writeByte(','); |
| 3586 | try writer.print(" {s}", .{fmtStringLiteral(clobber)}); |
| 3559 | 3587 | } |
| 3560 | 3588 | } |
| 3561 | 3589 | try writer.writeAll(");\n"); |
| 3562 | | try writer.writeAll("}\n"); |
| 3563 | 3590 | |
| 3564 | | if (f.liveness.isUnused(inst)) |
| 3565 | | return CValue.none; |
| 3591 | extra_i = constraints_extra_begin; |
| 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 | 3612 | fn airIsNull( |
| ... | ... | @@ -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 | 4707 | const FormatIntLiteralContext = struct { |
| 4638 | 4708 | ty: Type, |
| 4639 | 4709 | val: Value, |