| ... | ... | @@ -385,11 +385,6 @@ pub const DeclGen = struct { |
| 385 | 385 | // First try specific tag representations for more efficiency. |
| 386 | 386 | switch (val.tag()) { |
| 387 | 387 | .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"), |
| 388 | | .bytes => { |
| 389 | | const bytes = val.castTag(.bytes).?.data; |
| 390 | | // TODO: make our own C string escape instead of using std.zig.fmtEscapes |
| 391 | | try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}); |
| 392 | | }, |
| 393 | 388 | else => { |
| 394 | 389 | // Fall back to generic implementation. |
| 395 | 390 | var arena = std.heap.ArenaAllocator.init(dg.module.gpa); |
| ... | ... | @@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue { |
| 1449 | 1444 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1450 | 1445 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 1451 | 1446 | const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr(); |
| 1447 | |
| 1452 | 1448 | if (!is_volatile and f.liveness.isUnused(inst)) |
| 1453 | 1449 | return CValue.none; |
| 1450 | |
| 1454 | 1451 | const inst_ty = f.air.typeOfIndex(inst); |
| 1455 | | if (inst_ty.zigTypeTag() == .Array) |
| 1456 | | return f.fail("TODO: C backend: implement airLoad for arrays", .{}); |
| 1452 | const is_array = inst_ty.zigTypeTag() == .Array; |
| 1457 | 1453 | const operand = try f.resolveInst(ty_op.operand); |
| 1458 | 1454 | const writer = f.object.writer(); |
| 1459 | | const local = try f.allocLocal(inst_ty, .Const); |
| 1455 | |
| 1456 | // We need to separately initialize arrays with a memcpy so they must be mutable. |
| 1457 | const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); |
| 1458 | |
| 1460 | 1459 | switch (operand) { |
| 1461 | 1460 | .local_ref => |i| { |
| 1462 | 1461 | const wrapped: CValue = .{ .local = i }; |
| ... | ... | @@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1471 | 1470 | try writer.writeAll(";\n"); |
| 1472 | 1471 | }, |
| 1473 | 1472 | else => { |
| 1474 | | try writer.writeAll(" = *"); |
| 1475 | | try f.writeCValue(writer, operand); |
| 1476 | | try writer.writeAll(";\n"); |
| 1473 | if (is_array) { |
| 1474 | // Insert a memcpy to initialize this array. The source operand is always a pointer |
| 1475 | // and thus we only need to know size/type information from the local type/dest. |
| 1476 | try writer.writeAll(";"); |
| 1477 | try f.object.indent_writer.insertNewline(); |
| 1478 | try writer.writeAll("memcpy("); |
| 1479 | try f.writeCValue(writer, local); |
| 1480 | try writer.writeAll(", "); |
| 1481 | try f.writeCValue(writer, operand); |
| 1482 | try writer.writeAll(", sizeof("); |
| 1483 | try f.writeCValue(writer, local); |
| 1484 | try writer.writeAll("));\n"); |
| 1485 | } else { |
| 1486 | try writer.writeAll(" = *"); |
| 1487 | try f.writeCValue(writer, operand); |
| 1488 | try writer.writeAll(";\n"); |
| 1489 | } |
| 1477 | 1490 | }, |
| 1478 | 1491 | } |
| 1479 | 1492 | return local; |
| ... | ... | @@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1580 | 1593 | return local; |
| 1581 | 1594 | } |
| 1582 | 1595 | |
| 1583 | | fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue { |
| 1596 | fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue { |
| 1584 | 1597 | const is_debug_build = f.object.dg.module.optimizeMode() == .Debug; |
| 1585 | 1598 | if (!is_debug_build) |
| 1586 | 1599 | return CValue.none; |
| ... | ... | @@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue { |
| 1604 | 1617 | try writer.writeAll("));\n"); |
| 1605 | 1618 | }, |
| 1606 | 1619 | else => { |
| 1607 | | const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*"; |
| 1620 | const indirection = if (dest_child_type.zigTypeTag() == .Array) "" else "*"; |
| 1608 | 1621 | |
| 1609 | 1622 | try writer.writeAll("memset("); |
| 1610 | 1623 | try f.writeCValue(writer, dest_ptr); |
| ... | ... | @@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1621 | 1634 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 1622 | 1635 | const dest_ptr = try f.resolveInst(bin_op.lhs); |
| 1623 | 1636 | const src_val = try f.resolveInst(bin_op.rhs); |
| 1624 | | const lhs_type = f.air.typeOf(bin_op.lhs); |
| 1637 | const lhs_child_type = f.air.typeOf(bin_op.lhs).childType(); |
| 1625 | 1638 | |
| 1626 | 1639 | // TODO Sema should emit a different instruction when the store should |
| 1627 | 1640 | // possibly do the safety 0xaa bytes for undefined. |
| 1628 | 1641 | const src_val_is_undefined = |
| 1629 | 1642 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; |
| 1630 | 1643 | if (src_val_is_undefined) |
| 1631 | | return try airStoreUndefined(f, dest_ptr, lhs_type); |
| 1632 | | |
| 1633 | | // Don't check this for airStoreUndefined as that will work for arrays already |
| 1634 | | if (lhs_type.childType().zigTypeTag() == .Array) |
| 1635 | | return f.fail("TODO: C backend: implement airStore for arrays", .{}); |
| 1644 | return try airStoreUndefined(f, dest_ptr, lhs_child_type); |
| 1636 | 1645 | |
| 1637 | 1646 | const writer = f.object.writer(); |
| 1638 | 1647 | switch (dest_ptr) { |
| ... | ... | @@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1651 | 1660 | try writer.writeAll(";\n"); |
| 1652 | 1661 | }, |
| 1653 | 1662 | else => { |
| 1654 | | try writer.writeAll("*"); |
| 1655 | | try f.writeCValue(writer, dest_ptr); |
| 1656 | | try writer.writeAll(" = "); |
| 1657 | | try f.writeCValue(writer, src_val); |
| 1658 | | try writer.writeAll(";\n"); |
| 1663 | if (lhs_child_type.zigTypeTag() == .Array) { |
| 1664 | // For this memcpy to safely work we need the rhs to have the same |
| 1665 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). |
| 1666 | const rhs_type = f.air.typeOf(bin_op.rhs); |
| 1667 | assert(rhs_type.eql(lhs_child_type)); |
| 1668 | |
| 1669 | // If the source is a constant, writeCValue will emit a brace initialization |
| 1670 | // so work around this by initializing into new local. |
| 1671 | // TODO this should be done by manually initializing elements of the dest array |
| 1672 | const array_src = if (src_val == .constant) blk: { |
| 1673 | const new_local = try f.allocLocal(rhs_type, .Const); |
| 1674 | try writer.writeAll(" = "); |
| 1675 | try f.writeCValue(writer, src_val); |
| 1676 | try writer.writeAll(";"); |
| 1677 | try f.object.indent_writer.insertNewline(); |
| 1678 | |
| 1679 | break :blk new_local; |
| 1680 | } else src_val; |
| 1681 | |
| 1682 | try writer.writeAll("memcpy("); |
| 1683 | try f.writeCValue(writer, dest_ptr); |
| 1684 | try writer.writeAll(", "); |
| 1685 | try f.writeCValue(writer, array_src); |
| 1686 | try writer.writeAll(", sizeof("); |
| 1687 | try f.writeCValue(writer, array_src); |
| 1688 | try writer.writeAll("));\n"); |
| 1689 | } else { |
| 1690 | try writer.writeAll("*"); |
| 1691 | try f.writeCValue(writer, dest_ptr); |
| 1692 | try writer.writeAll(" = "); |
| 1693 | try f.writeCValue(writer, src_val); |
| 1694 | try writer.writeAll(";\n"); |
| 1695 | } |
| 1659 | 1696 | }, |
| 1660 | 1697 | } |
| 1661 | 1698 | return CValue.none; |