Commit 95b8a5f157

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-08-25 22:17:57
stage2 ARM: extract remaining operations out of binOp
1 parent fdb2c80
Changed files (1)
src
arch
src/arch/arm/CodeGen.zig
@@ -1407,8 +1407,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
         const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
         const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
-        const lhs = try self.resolveInst(bin_op.lhs);
-        const rhs = try self.resolveInst(bin_op.rhs);
 
         break :result switch (tag) {
             .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
@@ -1427,11 +1425,24 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
 
             .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
 
-            else => try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
-                .lhs = bin_op.lhs,
-                .rhs = bin_op.rhs,
-                .inst = inst,
-            }),
+            .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+
+            .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+
+            .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+
+            .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+
+            .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+            .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
+
+            else => unreachable,
         };
     };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
@@ -1440,19 +1451,15 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
 fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
-    const lhs = try self.resolveInst(bin_op.lhs);
-    const rhs = try self.resolveInst(bin_op.rhs);
     const lhs_ty = self.air.typeOf(bin_op.lhs);
     const rhs_ty = self.air.typeOf(bin_op.rhs);
 
-    const result: MCValue = if (self.liveness.isUnused(inst))
-        .dead
-    else
-        try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
-            .lhs = bin_op.lhs,
-            .rhs = bin_op.rhs,
-            .inst = inst,
-        });
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
+        const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
+
+        break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
+    };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
@@ -2247,7 +2254,11 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
             },
             else => {
                 const dest = try self.allocRegOrMem(inst, true);
-                const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null);
+
+                const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
+                const index_bind: ReadArg.Bind = .{ .mcv = index_mcv };
+
+                const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ptr_field_type, Type.usize, null);
                 try self.load(dest, addr, slice_ptr_field_type);
 
                 break :result dest;
@@ -2262,12 +2273,15 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
         const slice_mcv = try self.resolveInst(extra.lhs);
-        const index_mcv = try self.resolveInst(extra.rhs);
         const base_mcv = slicePtr(slice_mcv);
 
+        const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
+        const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
+
         const slice_ty = self.air.typeOf(extra.lhs);
+        const index_ty = self.air.typeOf(extra.rhs);
 
-        const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null);
+        const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null);
         break :result addr;
     };
     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
@@ -2290,12 +2304,13 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
-        const ptr_mcv = try self.resolveInst(extra.lhs);
-        const index_mcv = try self.resolveInst(extra.rhs);
+        const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
+        const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
 
         const ptr_ty = self.air.typeOf(extra.lhs);
+        const index_ty = self.air.typeOf(extra.rhs);
 
-        const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null);
+        const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null);
         break :result addr;
     };
     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
@@ -3219,240 +3234,6 @@ const BinOpMetadata = struct {
     rhs: Air.Inst.Ref,
 };
 
-/// For all your binary operation needs, this function will generate
-/// the corresponding Mir instruction(s). Returns the location of the
-/// result.
-///
-/// If the binary operation itself happens to be an Air instruction,
-/// pass the corresponding index in the inst parameter. That helps
-/// this function do stuff like reusing operands.
-///
-/// This function does not do any lowering to Mir itself, but instead
-/// looks at the lhs and rhs and determines which kind of lowering
-/// would be best suitable and then delegates the lowering to other
-/// functions.
-fn binOp(
-    self: *Self,
-    tag: Air.Inst.Tag,
-    lhs: MCValue,
-    rhs: MCValue,
-    lhs_ty: Type,
-    rhs_ty: Type,
-    metadata: ?BinOpMetadata,
-) InnerError!MCValue {
-    switch (tag) {
-        .addwrap,
-        .subwrap,
-        .mulwrap,
-        => {
-            const base_tag: Air.Inst.Tag = switch (tag) {
-                .addwrap => .add,
-                .subwrap => .sub,
-                .mulwrap => .mul,
-                else => unreachable,
-            };
-
-            const lhs_bind = if (metadata) |md|
-                ReadArg.Bind{ .inst = md.lhs }
-            else
-                ReadArg.Bind{ .mcv = lhs };
-            const rhs_bind = if (metadata) |md|
-                ReadArg.Bind{ .inst = md.rhs }
-            else
-                ReadArg.Bind{ .mcv = rhs };
-
-            // Generate an add/sub/mul
-            const maybe_inst: ?Air.Inst.Index = if (metadata) |md| md.inst else null;
-            const result = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
-
-            // Truncate if necessary
-            switch (lhs_ty.zigTypeTag()) {
-                .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
-                .Int => {
-                    const int_info = lhs_ty.intInfo(self.target.*);
-                    if (int_info.bits <= 32) {
-                        const result_reg = result.register;
-
-                        if (int_info.bits < 32) {
-                            try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
-                            return result;
-                        } else return result;
-                    } else {
-                        return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
-                    }
-                },
-                else => unreachable,
-            }
-        },
-        .bit_and,
-        .bit_or,
-        .xor,
-        => {
-            switch (lhs_ty.zigTypeTag()) {
-                .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
-                .Int => {
-                    const mod = self.bin_file.options.module.?;
-                    assert(lhs_ty.eql(rhs_ty, mod));
-                    const int_info = lhs_ty.intInfo(self.target.*);
-                    if (int_info.bits <= 32) {
-                        const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
-                        const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null;
-
-                        const mir_tag: Mir.Inst.Tag = switch (tag) {
-                            .bit_and => .@"and",
-                            .bit_or => .orr,
-                            .xor => .eor,
-                            else => unreachable,
-                        };
-
-                        if (rhs_immediate_ok) {
-                            return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
-                        } else if (lhs_immediate_ok) {
-                            // swap lhs and rhs
-                            return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
-                        } else {
-                            return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
-                        }
-                    } else {
-                        return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
-                    }
-                },
-                else => unreachable,
-            }
-        },
-        .shl_exact,
-        .shr_exact,
-        => {
-            switch (lhs_ty.zigTypeTag()) {
-                .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
-                .Int => {
-                    const int_info = lhs_ty.intInfo(self.target.*);
-                    if (int_info.bits <= 32) {
-                        const rhs_immediate_ok = rhs == .immediate;
-
-                        const mir_tag: Mir.Inst.Tag = switch (tag) {
-                            .shl_exact => .lsl,
-                            .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
-                                .signed => Mir.Inst.Tag.asr,
-                                .unsigned => Mir.Inst.Tag.lsr,
-                            },
-                            else => unreachable,
-                        };
-
-                        if (rhs_immediate_ok) {
-                            return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
-                        } else {
-                            return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
-                        }
-                    } else {
-                        return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
-                    }
-                },
-                else => unreachable,
-            }
-        },
-        .shl,
-        .shr,
-        => {
-            const base_tag: Air.Inst.Tag = switch (tag) {
-                .shl => .shl_exact,
-                .shr => .shr_exact,
-                else => unreachable,
-            };
-
-            // Generate a shl_exact/shr_exact
-            const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
-
-            // Truncate if necessary
-            switch (tag) {
-                .shr => return result,
-                .shl => switch (lhs_ty.zigTypeTag()) {
-                    .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
-                    .Int => {
-                        const int_info = lhs_ty.intInfo(self.target.*);
-                        if (int_info.bits <= 32) {
-                            const result_reg = result.register;
-
-                            if (int_info.bits < 32) {
-                                try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
-                                return result;
-                            } else return result;
-                        } else {
-                            return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
-                        }
-                    },
-                    else => unreachable,
-                },
-                else => unreachable,
-            }
-        },
-        .bool_and,
-        .bool_or,
-        => {
-            switch (lhs_ty.zigTypeTag()) {
-                .Bool => {
-                    const lhs_immediate_ok = lhs == .immediate;
-                    const rhs_immediate_ok = rhs == .immediate;
-
-                    const mir_tag: Mir.Inst.Tag = switch (tag) {
-                        .bool_and => .@"and",
-                        .bool_or => .orr,
-                        else => unreachable,
-                    };
-
-                    if (rhs_immediate_ok) {
-                        return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
-                    } else if (lhs_immediate_ok) {
-                        // swap lhs and rhs
-                        return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
-                    } else {
-                        return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
-                    }
-                },
-                else => unreachable,
-            }
-        },
-        .ptr_add,
-        .ptr_sub,
-        => {
-            switch (lhs_ty.zigTypeTag()) {
-                .Pointer => {
-                    const ptr_ty = lhs_ty;
-                    const elem_ty = switch (ptr_ty.ptrSize()) {
-                        .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
-                        else => ptr_ty.childType(),
-                    };
-                    const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
-
-                    if (elem_size == 1) {
-                        const base_tag: Mir.Inst.Tag = switch (tag) {
-                            .ptr_add => .add,
-                            .ptr_sub => .sub,
-                            else => unreachable,
-                        };
-
-                        return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
-                    } else {
-                        // convert the offset into a byte offset by
-                        // multiplying it with elem_size
-                        const rhs_bind = if (metadata) |md|
-                            ReadArg.Bind{ .inst = md.rhs }
-                        else
-                            ReadArg.Bind{ .mcv = rhs };
-                        const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
-
-                        const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
-                        const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);
-                        return addr;
-                    }
-                },
-                else => unreachable,
-            }
-        },
-        else => unreachable,
-    }
-}
-
 fn addSub(
     self: *Self,
     tag: Air.Inst.Tag,
@@ -3713,6 +3494,248 @@ fn modulo(
     }
 }
 
+fn wrappingArithmetic(
+    self: *Self,
+    tag: Air.Inst.Tag,
+    lhs_bind: ReadArg.Bind,
+    rhs_bind: ReadArg.Bind,
+    lhs_ty: Type,
+    rhs_ty: Type,
+    maybe_inst: ?Air.Inst.Index,
+) InnerError!MCValue {
+    const base_tag: Air.Inst.Tag = switch (tag) {
+        .addwrap => .add,
+        .subwrap => .sub,
+        .mulwrap => .mul,
+        else => unreachable,
+    };
+
+    // Generate an add/sub/mul
+    const result = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
+
+    // Truncate if necessary
+    switch (lhs_ty.zigTypeTag()) {
+        .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
+        .Int => {
+            const int_info = lhs_ty.intInfo(self.target.*);
+            if (int_info.bits <= 32) {
+                const result_reg = result.register;
+
+                if (int_info.bits < 32) {
+                    try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
+                    return result;
+                } else return result;
+            } else {
+                return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
+            }
+        },
+        else => unreachable,
+    }
+}
+
+fn bitwise(
+    self: *Self,
+    tag: Air.Inst.Tag,
+    lhs_bind: ReadArg.Bind,
+    rhs_bind: ReadArg.Bind,
+    lhs_ty: Type,
+    rhs_ty: Type,
+    maybe_inst: ?Air.Inst.Index,
+) InnerError!MCValue {
+    switch (lhs_ty.zigTypeTag()) {
+        .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
+        .Int => {
+            const mod = self.bin_file.options.module.?;
+            assert(lhs_ty.eql(rhs_ty, mod));
+            const int_info = lhs_ty.intInfo(self.target.*);
+            if (int_info.bits <= 32) {
+                const lhs_immediate = try lhs_bind.resolveToImmediate(self);
+                const rhs_immediate = try rhs_bind.resolveToImmediate(self);
+
+                const lhs_immediate_ok = if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
+                const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
+
+                const mir_tag: Mir.Inst.Tag = switch (tag) {
+                    .bit_and => .@"and",
+                    .bit_or => .orr,
+                    .xor => .eor,
+                    else => unreachable,
+                };
+
+                if (rhs_immediate_ok) {
+                    return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
+                } else if (lhs_immediate_ok) {
+                    // swap lhs and rhs
+                    return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
+                } else {
+                    return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
+                }
+            } else {
+                return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
+            }
+        },
+        else => unreachable,
+    }
+}
+
+fn shiftExact(
+    self: *Self,
+    tag: Air.Inst.Tag,
+    lhs_bind: ReadArg.Bind,
+    rhs_bind: ReadArg.Bind,
+    lhs_ty: Type,
+    rhs_ty: Type,
+    maybe_inst: ?Air.Inst.Index,
+) InnerError!MCValue {
+    switch (lhs_ty.zigTypeTag()) {
+        .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
+        .Int => {
+            const int_info = lhs_ty.intInfo(self.target.*);
+            if (int_info.bits <= 32) {
+                const rhs_immediate = try rhs_bind.resolveToImmediate(self);
+
+                const mir_tag: Mir.Inst.Tag = switch (tag) {
+                    .shl_exact => .lsl,
+                    .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
+                        .signed => Mir.Inst.Tag.asr,
+                        .unsigned => Mir.Inst.Tag.lsr,
+                    },
+                    else => unreachable,
+                };
+
+                if (rhs_immediate) |imm| {
+                    return try self.binOpImmediateNew(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
+                } else {
+                    return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
+                }
+            } else {
+                return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
+            }
+        },
+        else => unreachable,
+    }
+}
+
+fn shiftNormal(
+    self: *Self,
+    tag: Air.Inst.Tag,
+    lhs_bind: ReadArg.Bind,
+    rhs_bind: ReadArg.Bind,
+    lhs_ty: Type,
+    rhs_ty: Type,
+    maybe_inst: ?Air.Inst.Index,
+) InnerError!MCValue {
+    const base_tag: Air.Inst.Tag = switch (tag) {
+        .shl => .shl_exact,
+        .shr => .shr_exact,
+        else => unreachable,
+    };
+
+    // Generate a shl_exact/shr_exact
+    const result = try self.shiftExact(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
+
+    // Truncate if necessary
+    switch (tag) {
+        .shr => return result,
+        .shl => switch (lhs_ty.zigTypeTag()) {
+            .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
+            .Int => {
+                const int_info = lhs_ty.intInfo(self.target.*);
+                if (int_info.bits <= 32) {
+                    const result_reg = result.register;
+
+                    if (int_info.bits < 32) {
+                        try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
+                        return result;
+                    } else return result;
+                } else {
+                    return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
+                }
+            },
+            else => unreachable,
+        },
+        else => unreachable,
+    }
+}
+
+fn booleanOp(
+    self: *Self,
+    tag: Air.Inst.Tag,
+    lhs_bind: ReadArg.Bind,
+    rhs_bind: ReadArg.Bind,
+    lhs_ty: Type,
+    rhs_ty: Type,
+    maybe_inst: ?Air.Inst.Index,
+) InnerError!MCValue {
+    switch (lhs_ty.zigTypeTag()) {
+        .Bool => {
+            const lhs_immediate = try lhs_bind.resolveToImmediate(self);
+            const rhs_immediate = try rhs_bind.resolveToImmediate(self);
+
+            const mir_tag: Mir.Inst.Tag = switch (tag) {
+                .bool_and => .@"and",
+                .bool_or => .orr,
+                else => unreachable,
+            };
+
+            if (rhs_immediate) |imm| {
+                return try self.binOpImmediateNew(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
+            } else if (lhs_immediate) |imm| {
+                // swap lhs and rhs
+                return try self.binOpImmediateNew(mir_tag, rhs_bind, imm, rhs_ty, true, maybe_inst);
+            } else {
+                return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
+            }
+        },
+        else => unreachable,
+    }
+}
+
+fn ptrArithmetic(
+    self: *Self,
+    tag: Air.Inst.Tag,
+    lhs_bind: ReadArg.Bind,
+    rhs_bind: ReadArg.Bind,
+    lhs_ty: Type,
+    rhs_ty: Type,
+    maybe_inst: ?Air.Inst.Index,
+) InnerError!MCValue {
+    switch (lhs_ty.zigTypeTag()) {
+        .Pointer => {
+            const mod = self.bin_file.options.module.?;
+            assert(rhs_ty.eql(Type.usize, mod));
+
+            const ptr_ty = lhs_ty;
+            const elem_ty = switch (ptr_ty.ptrSize()) {
+                .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
+                else => ptr_ty.childType(),
+            };
+            const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
+
+            const base_tag: Air.Inst.Tag = switch (tag) {
+                .ptr_add => .add,
+                .ptr_sub => .sub,
+                else => unreachable,
+            };
+
+            if (elem_size == 1) {
+                return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst);
+            } else {
+                // convert the offset into a byte offset by
+                // multiplying it with elem_size
+                const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
+
+                const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
+                const offset_bind = ReadArg.Bind{ .mcv = offset };
+
+                const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null);
+                return addr;
+            }
+        },
+        else => unreachable,
+    }
+}
+
 fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void {
     const abi_size = ty.abiSize(self.target.*);
 
@@ -4614,7 +4637,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
             if (else_value == .dead)
                 continue;
             // The instruction is only overridden in the else branch.
-            var i: usize = self.branch_stack.items.len - 2;
+            var i: usize = self.branch_stack.items.len - 1;
             while (true) {
                 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
                 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
@@ -4641,7 +4664,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
         if (then_value == .dead)
             continue;
         const parent_mcv = blk: {
-            var i: usize = self.branch_stack.items.len - 2;
+            var i: usize = self.branch_stack.items.len - 1;
             while (true) {
                 i -= 1;
                 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {