Skip to content

Sema: correct AIR semantics around array concat optimization#25594

Open
Rexicon226 wants to merge 1 commit into
ziglang:masterfrom
Rexicon226:array-cat-fix
Open

Sema: correct AIR semantics around array concat optimization#25594
Rexicon226 wants to merge 1 commit into
ziglang:masterfrom
Rexicon226:array-cat-fix

Conversation

@Rexicon226

Copy link
Copy Markdown
Contributor

closes #24953

New AIR
export fn a(x: *const [0]u8) void {
    _ = @as([]const u8, "") ++ x;
}

->

  %0!= arg(*const [0]u8, 0)
  %1!= dbg_stmt(2:5)
  %4!= alloc(*[0]u8)
  %5!= ret(@.void_value)

export fn b(x: *const [1]u8) void {
    _ = @as([]const u8, "") ++ x;
}

->

  %0 = arg(*const [1]u8, 0)
  %1!= dbg_stmt(2:5)
  %4 = alloc(*[1]u8)
  %5!= bitcast([*]u8, %4)
  %6 = array_to_slice([]u8, %4!)
  %7!= memcpy(%6!, %0!)
  %8!= bitcast(*const [1]u8, %4)
  %9!= ret(@.void_value)

export fn c(x: *const [0]u8) void {
    _ = @as([]const u8, "1") ++ x;
}

->

  %0!= arg(*const [0]u8, 0)
  %1!= dbg_stmt(2:5)
  %4 = alloc(*[1]u8)
  %5 = array_to_slice([]u8, %4!)
  %6 = slice_ptr([*]u8, <[]const u8, "1"[0..1]>)
  %7!= memcpy(%5!, %6!)
  %8!= bitcast(*const [1]u8, %4)
  %9!= ret(@.void_value)

export fn d(x: *const [0]u8) void {
    _ = "1" ++ x;
}

->

  %0!= arg(*const [0]u8, 0)
  %1!= dbg_stmt(2:5)
  %4 = alloc(*[1:0]u8)
  %5 = array_to_slice([]u8, %4)
  %6!= memcpy(%5!, <*const [1:0]u8, "1">)
  %7 = ptr_elem_ptr(*u8, %4!, @.one_usize)
  %8!= store(%7!, @.zero_u8)
  %9!= bitcast(*const [1:0]u8, %4)
  %10!= ret(@.void_value)

cc @jacobly0, perhaps you could double check my AIR?

@jacobly0 jacobly0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  %0 = arg(*const [1]u8, 0)
  %6 = array_to_slice([]u8, %4!)
  %7!= memcpy(%6!, %0!)

This still seems wrong, when either side is a pointer-to-array (aka comptime-known length), lhs must be pointer-to-array and rhs must be a non-slice pointer, see for reference:

comptime {
    _ = &memcpy;
}
fn memcpy(lhs: []u8, rhs: *const [5]u8) void {
    @setRuntimeSafety(false);
    @memcpy(lhs, rhs);
}
$ zig build-obj repro.zig --verbose-air -fstrip
# Begin Function AIR: repro.memcpy:
# Total AIR+Liveness bytes: 219B
# AIR Instructions:         7 (63B)
# AIR Extra Data:           9 (36B)
# Liveness tomb_bits:       8B
# Liveness Extra Data:      0 (0B)
# Liveness special table:   0 (0B)
  %0 = arg([]u8, 0)
  %1 = arg(*const [5]u8, 1)
  %2!= slice_len(usize, %0)
  %3 = slice_ptr([*]u8, %0!)
  %4 = bitcast(*[5]u8, %3!)
  %5!= memcpy(%4!, %1!)
  %6!= ret(@.void_value)
# End Function AIR: repro.memcpy
@Rexicon226

Rexicon226 commented Oct 16, 2025

Copy link
Copy Markdown
Contributor Author

Ok I've revised it with my new understanding of the semantics...

# Begin Function AIR: empty.b:
# Total AIR+Liveness bytes: 211B
# AIR Instructions:         7 (63B)
# AIR Extra Data:           7 (28B)
# Liveness tomb_bits:       8B
# Liveness Extra Data:      0 (0B)
# Liveness special table:   0 (0B)
  %0 = arg(*const [1]u8, 0)
  %3 = alloc(*[1]u8)
  %4!= memcpy(%3!, %0!)
  %5!= bitcast(*const [1]u8, %3)
  %6!= ret(@.void_value)
# End Function AIR: empty.b

and,

export fn foo(x: *const [1]u8) void {
    _ = @as([]const u8, "1") ++ x;
}
  %0 = arg(*const [1]u8, 0)
  %3 = alloc(*[2]u8)
  %4 = bitcast(*[1]u8, %3)
  %5 = slice_ptr([*]u8, <[]const u8, "1"[0..1]>)
  %6!= memcpy(%4!, %5!)
  %7 = bitcast([*]u8, %3!)
  %8 = ptr_add([*]u8, %7!, @.one_usize)
  %9 = bitcast(*[1]u8, %8!)
  %10!= memcpy(%9!, %0!)
  %11!= bitcast(*const [2]u8, %3)
  %12!= ret(@.void_value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants