Skip to content

goto between def and use may cause CIR generate multiple definition's code: operand #0 does not dominate this use #883

@ChuanqiXu9

Description

@ChuanqiXu9

Reproducer:

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s

struct def;
typedef struct def *decl;
struct def {
  int index;
};
struct def d;
int foo(unsigned char cond)
{
  if (cond)
    goto label;

  {
    decl b = &d;

    label:
      return b->index;
  }

  return 0;
}

The generated CIR looks like:

cir.func  @foo(%arg0: !u8i loc(fused[#loc5, #loc6])) -> !s32i extra(#fn_attr) {
    %0 = cir.alloca !u8i, !cir.ptr<!u8i>, ["cond", init] {alignment = 1 : i64} loc(#loc25)
    %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} loc(#loc4)
    cir.store %arg0, %0 : !u8i, !cir.ptr<!u8i> loc(#loc7)
    cir.scope {
      %4 = cir.load %0 : !cir.ptr<!u8i>, !u8i loc(#loc10)
      %5 = cir.cast(int_to_bool, %4 : !u8i), !cir.bool loc(#loc10)
      cir.if %5 {
        cir.goto "label" loc(#loc27)
      } loc(#loc27)
    } loc(#loc26)
    cir.scope {
      %4 = cir.alloca !cir.ptr<!ty_def>, !cir.ptr<!cir.ptr<!ty_def>>, ["b", init] {alignment = 8 : i64} loc(#loc29)
      %5 = cir.get_global @d : !cir.ptr<!ty_def> loc(#loc23)
      cir.store %5, %4 : !cir.ptr<!ty_def>, !cir.ptr<!cir.ptr<!ty_def>> loc(#loc29)
      cir.br ^bb1 loc(#loc16)
    ^bb1:  // pred: ^bb0
      cir.label "label" loc(#loc16)
      %6 = cir.load %4 : !cir.ptr<!cir.ptr<!ty_def>>, !cir.ptr<!ty_def> loc(#loc17)
      %7 = cir.get_member %6[0] {name = "index"} : !cir.ptr<!ty_def> -> !cir.ptr<!s32i> loc(#loc18)
      %8 = cir.load %7 : !cir.ptr<!s32i>, !s32i loc(#loc19)
      cir.store %8, %1 : !s32i, !cir.ptr<!s32i> loc(#loc30)
      %9 = cir.load %1 : !cir.ptr<!s32i>, !s32i loc(#loc30)
      cir.return %9 : !s32i loc(#loc30)
    } loc(#loc28)
    %2 = cir.const #cir.int<0> : !s32i loc(#loc21)
    cir.store %2, %1 : !s32i, !cir.ptr<!s32i> loc(#loc31)
    %3 = cir.load %1 : !cir.ptr<!s32i>, !s32i loc(#loc31)
    cir.return %3 : !s32i loc(#loc31)
  } loc(#loc24)

We can find the def %4 and %5 are defined twice.

FYI, the generated LLVM IR without CIR pipeline is:

define dso_local i32 @foo(i8 noundef zeroext %cond) #0 {
entry:
  %cond.addr = alloca i8, align 1
  %b = alloca ptr, align 8
  store i8 %cond, ptr %cond.addr, align 1
  %0 = load i8, ptr %cond.addr, align 1
  %tobool = icmp ne i8 %0, 0
  br i1 %tobool, label %if.then, label %if.end

if.then:                                          ; preds = %entry
  br label %label

if.end:                                           ; preds = %entry
  store ptr @d, ptr %b, align 8
  br label %label

label:                                            ; preds = %if.end, %if.then
  %1 = load ptr, ptr %b, align 8
  %index = getelementptr inbounds %struct.def, ptr %1, i32 0, i32 0
  %2 = load i32, ptr %index, align 4
  ret i32 %2
}

It is a UB if cond is true. But we should never crash.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions