Skip to content

Emit LLVM lifetime intrinsics #1129

Open
@ChuanqiXu9

Description

@ChuanqiXu9

For

struct S {
    int a;
    int b;
};

S getS();

S getS2(const S&);

S foo() {
    return getS2(getS());
}

Compile it with -fclangir -O3 -emit-cir, we got:

cir.func @_Z3foov() -> !ty_S extra(#fn_attr) {
    %0 = cir.alloca !ty_S, !cir.ptr<!ty_S>, ["__retval"] {alignment = 4 : i64} loc(#loc6)
    cir.scope {
      %2 = cir.alloca !ty_S, !cir.ptr<!ty_S>, ["ref.tmp0"] {alignment = 4 : i64} loc(#loc16)
      %3 = cir.call @_Z4getSv() : () -> !ty_S loc(#loc9)
      cir.store %3, %2 : !ty_S, !cir.ptr<!ty_S> loc(#loc9)
      %4 = cir.call @_Z5getS2RK1S(%2) : (!cir.ptr<!ty_S>) -> !ty_S loc(#loc7)
      cir.store %4, %0 : !ty_S, !cir.ptr<!ty_S> loc(#loc7)
    } loc(#loc15)
    %1 = cir.load %0 : !cir.ptr<!ty_S>, !ty_S loc(#loc17)
    cir.return %1 : !ty_S loc(#loc17)
  }

This is good. We explicitly marked the scope for the temporary. However, when we lower it to LLVM, we lost the lifetime informations:

define dso_local %struct.S @_Z3foov() #0 !dbg !7 {
  %1 = alloca %struct.S, i64 1, align 4, !dbg !8
  %2 = alloca %struct.S, i64 1, align 4, !dbg !9
  br label %3, !dbg !8

3:                                                ; preds = %0
  %4 = call %struct.S @_Z4getSv(), !dbg !10
  store %struct.S %4, ptr %1, align 4, !dbg !10
  %5 = call %struct.S @_Z5getS2RK1S(ptr %1), !dbg !11
  store %struct.S %5, ptr %2, align 4, !dbg !11
  br label %6, !dbg !12

6:                                                ; preds = %3
  %7 = load %struct.S, ptr %2, align 4, !dbg !8
  ret %struct.S %7, !dbg !8
}

In the contrary, the emitted LLVM without clangir is:

define dso_local i64 @_Z3foov() #0 {
entry:
  %retval = alloca %struct.S, align 4
  %ref.tmp = alloca %struct.S, align 4
  call void @llvm.lifetime.start.p0(i64 8, ptr %ref.tmp) #3
  %call = call i64 @_Z4getSv()
  store i64 %call, ptr %ref.tmp, align 4
  %call1 = call i64 @_Z5getS2RK1S(ptr noundef nonnull align 4 dereferenceable(8) %ref.tmp)
  store i64 %call1, ptr %retval, align 4
  call void @llvm.lifetime.end.p0(i64 8, ptr %ref.tmp) #3
  %0 = load i64, ptr %retval, align 4
  ret i64 %0
}

where we can see the lifetime markers pretty clearly. The lifetime markers are pretty important for optimizations in the middle end.

We need to do this especially we've already marked the scope clearly.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions