| ... | @@ -385,11 +385,6 @@ pub const DeclGen = struct { | ... | @@ -385,11 +385,6 @@ pub const DeclGen = struct { |
| 385 | // First try specific tag representations for more efficiency. | 385 | // First try specific tag representations for more efficiency. |
| 386 | switch (val.tag()) { | 386 | switch (val.tag()) { |
| 387 | .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"), | 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 | else => { | 388 | else => { |
| 394 | // Fall back to generic implementation. | 389 | // Fall back to generic implementation. |
| 395 | var arena = std.heap.ArenaAllocator.init(dg.module.gpa); | 390 | var arena = std.heap.ArenaAllocator.init(dg.module.gpa); |
| ... | @@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue { | ... | @@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue { |
| 1449 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | 1444 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1450 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 1445 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 1451 | const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr(); | 1446 | const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr(); |
| | 1447 | |
| 1452 | if (!is_volatile and f.liveness.isUnused(inst)) | 1448 | if (!is_volatile and f.liveness.isUnused(inst)) |
| 1453 | return CValue.none; | 1449 | return CValue.none; |
| | 1450 | |
| 1454 | const inst_ty = f.air.typeOfIndex(inst); | 1451 | const inst_ty = f.air.typeOfIndex(inst); |
| 1455 | if (inst_ty.zigTypeTag() == .Array) | 1452 | const is_array = inst_ty.zigTypeTag() == .Array; |
| 1456 | return f.fail("TODO: C backend: implement airLoad for arrays", .{}); | | |
| 1457 | const operand = try f.resolveInst(ty_op.operand); | 1453 | const operand = try f.resolveInst(ty_op.operand); |
| 1458 | const writer = f.object.writer(); | 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 | switch (operand) { | 1459 | switch (operand) { |
| 1461 | .local_ref => |i| { | 1460 | .local_ref => |i| { |
| 1462 | const wrapped: CValue = .{ .local = i }; | 1461 | const wrapped: CValue = .{ .local = i }; |
| ... | @@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1471 | try writer.writeAll(";\n"); | 1470 | try writer.writeAll(";\n"); |
| 1472 | }, | 1471 | }, |
| 1473 | else => { | 1472 | else => { |
| 1474 | try writer.writeAll(" = *"); | 1473 | if (is_array) { |
| 1475 | try f.writeCValue(writer, operand); | 1474 | // Insert a memcpy to initialize this array. The source operand is always a pointer |
| 1476 | try writer.writeAll(";\n"); | 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 | return local; | 1492 | return local; |
| ... | @@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1580 | return local; | 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 | const is_debug_build = f.object.dg.module.optimizeMode() == .Debug; | 1597 | const is_debug_build = f.object.dg.module.optimizeMode() == .Debug; |
| 1585 | if (!is_debug_build) | 1598 | if (!is_debug_build) |
| 1586 | return CValue.none; | 1599 | return CValue.none; |
| ... | @@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue { | ... | @@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue { |
| 1604 | try writer.writeAll("));\n"); | 1617 | try writer.writeAll("));\n"); |
| 1605 | }, | 1618 | }, |
| 1606 | else => { | 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 | try writer.writeAll("memset("); | 1622 | try writer.writeAll("memset("); |
| 1610 | try f.writeCValue(writer, dest_ptr); | 1623 | try f.writeCValue(writer, dest_ptr); |
| ... | @@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1621 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 1634 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 1622 | const dest_ptr = try f.resolveInst(bin_op.lhs); | 1635 | const dest_ptr = try f.resolveInst(bin_op.lhs); |
| 1623 | const src_val = try f.resolveInst(bin_op.rhs); | 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 | // TODO Sema should emit a different instruction when the store should | 1639 | // TODO Sema should emit a different instruction when the store should |
| 1627 | // possibly do the safety 0xaa bytes for undefined. | 1640 | // possibly do the safety 0xaa bytes for undefined. |
| 1628 | const src_val_is_undefined = | 1641 | const src_val_is_undefined = |
| 1629 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; | 1642 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; |
| 1630 | if (src_val_is_undefined) | 1643 | if (src_val_is_undefined) |
| 1631 | return try airStoreUndefined(f, dest_ptr, lhs_type); | 1644 | return try airStoreUndefined(f, dest_ptr, lhs_child_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", .{}); | | |
| 1636 | | 1645 | |
| 1637 | const writer = f.object.writer(); | 1646 | const writer = f.object.writer(); |
| 1638 | switch (dest_ptr) { | 1647 | switch (dest_ptr) { |
| ... | @@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1651 | try writer.writeAll(";\n"); | 1660 | try writer.writeAll(";\n"); |
| 1652 | }, | 1661 | }, |
| 1653 | else => { | 1662 | else => { |
| 1654 | try writer.writeAll("*"); | 1663 | if (lhs_child_type.zigTypeTag() == .Array) { |
| 1655 | try f.writeCValue(writer, dest_ptr); | 1664 | // For this memcpy to safely work we need the rhs to have the same |
| 1656 | try writer.writeAll(" = "); | 1665 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). |
| 1657 | try f.writeCValue(writer, src_val); | 1666 | const rhs_type = f.air.typeOf(bin_op.rhs); |
| 1658 | try writer.writeAll(";\n"); | 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 | return CValue.none; | 1698 | return CValue.none; |