This implements an autoupgrade from constant expressions to instructions, which is needed for https://discourse.llvm.org/t/rfc-remove-most-constant-expressions/63179. The basic approach is that constant expressions (CST_CODE_CE_* records) now initially only create a BitcodeConstant value that holds opcode, flags and operands IDs. Then, when the value actually gets used, it can be converted either into a constant expression (if that expression type is still supported) or into a sequence of instructions. As currently all expressions are still supported, -expand-constant-exprs is added for testing purposes, to force expansion. PHI nodes require special handling, because the constant expression needs to be evaluated on the incoming edge. We do this by putting it into a temporary block and then wiring it up appropriately afterwards (for non-critical edges, we could also move the instructions into the predecessor). This also removes the need for the forward referenced constants machinery, as the BitcodeConstants only hold value IDs. At the point where the value is actually materialized, no forward references are needed anymore. Differential Revision: https://reviews.llvm.org/D127729
225 lines
7.2 KiB
LLVM
225 lines
7.2 KiB
LLVM
; RUN: llvm-as < %s | llvm-dis -expand-constant-exprs | FileCheck %s
|
|
|
|
@g = extern_weak global i32
|
|
@g2 = extern_weak global i32
|
|
|
|
define i64 @test_cast() {
|
|
; CHECK-LABEL: define i64 @test_cast() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: ret i64 %constexpr
|
|
ret i64 ptrtoint (ptr @g to i64)
|
|
}
|
|
|
|
define i1 @test_icmp() {
|
|
; CHECK-LABEL: define i1 @test_icmp() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = icmp ne i64 %constexpr, 0
|
|
; CHECK-NEXT: ret i1 %constexpr1
|
|
ret i1 icmp ne (i64 ptrtoint (ptr @g to i64), i64 0)
|
|
}
|
|
|
|
define i32 @test_select() {
|
|
; CHECK-LABEL: define i32 @test_select() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = icmp ne i64 %constexpr, 0
|
|
; CHECK-NEXT: %constexpr2 = select i1 %constexpr1, i32 1, i32 2
|
|
; CHECK-NEXT: ret i32 %constexpr2
|
|
ret i32 select (i1 icmp ne (i64 ptrtoint (ptr @g to i64), i64 0), i32 1, i32 2)
|
|
}
|
|
|
|
define i8 @test_extractelement() {
|
|
; CHECK-LABEL: define i8 @test_extractelement() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = icmp ne i64 %constexpr, 0
|
|
; CHECK-NEXT: %constexpr2 = select i1 %constexpr1, <2 x i8> zeroinitializer, <2 x i8> <i8 0, i8 1>
|
|
; CHECK-NEXT: %constexpr3 = extractelement <2 x i8> %constexpr2, i32 0
|
|
; CHECK-NEXT: ret i8 %constexpr3
|
|
ret i8 extractelement (<2 x i8> select (i1 icmp ne (i64 ptrtoint (ptr @g to i64), i64 0), <2 x i8> zeroinitializer, <2 x i8> <i8 0, i8 1>), i32 0)
|
|
}
|
|
|
|
define <2 x i8> @test_insertelement() {
|
|
; CHECK-LABEL: define <2 x i8> @test_insertelement() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i32
|
|
; CHECK-NEXT: %constexpr1 = insertelement <2 x i8> poison, i8 42, i32 %constexpr
|
|
; CHECK-NEXT: ret <2 x i8> %constexpr1
|
|
ret <2 x i8> insertelement (<2 x i8> poison, i8 42, i32 ptrtoint (ptr @g to i32))
|
|
}
|
|
|
|
define double @test_fneg() {
|
|
; CHECK-LABEL: define double @test_fneg() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = bitcast i64 %constexpr to double
|
|
; CHECK-NEXT: %constexpr2 = fneg double %constexpr1
|
|
ret double fneg (double bitcast (i64 ptrtoint (ptr @g to i64) to double))
|
|
}
|
|
|
|
define i64 @test_flags() {
|
|
; CHECK-LABEL: define i64 @test_flags() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = add nuw i64 %constexpr, 1
|
|
; CHECK-NEXT: ret i64 %constexpr1
|
|
ret i64 add nuw (i64 ptrtoint (ptr @g to i64), i64 1)
|
|
}
|
|
|
|
define <3 x i64> @test_vector() {
|
|
; CHECK-LABEL: define <3 x i64> @test_vector() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr.ins = insertelement <3 x i64> poison, i64 5, i32 0
|
|
; CHECK-NEXT: %constexpr.ins1 = insertelement <3 x i64> %constexpr.ins, i64 %constexpr, i32 1
|
|
; CHECK-NEXT: %constexpr.ins2 = insertelement <3 x i64> %constexpr.ins1, i64 7, i32 2
|
|
ret <3 x i64> <i64 5, i64 ptrtoint (ptr @g to i64), i64 7>
|
|
}
|
|
|
|
define i64 @test_reused_expr() {
|
|
; CHECK-LABEL: define i64 @test_reused_expr() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = add i64 %constexpr, %constexpr
|
|
; CHECK-NEXT: ret i64 %constexpr1
|
|
ret i64 add (i64 ptrtoint (ptr @g to i64), i64 ptrtoint (ptr @g to i64))
|
|
}
|
|
|
|
define i64 @test_multiple_expanded_operands() {
|
|
; CHECK-LABEL: define i64 @test_multiple_expanded_operands() {
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr1 = ptrtoint ptr @g2 to i64
|
|
; CHECK-NEXT: %constexpr2 = add i64 %constexpr, %constexpr1
|
|
; CHECK-NEXT: ret i64 %constexpr2
|
|
ret i64 add (i64 ptrtoint (ptr @g to i64), i64 ptrtoint (ptr @g2 to i64))
|
|
}
|
|
|
|
define i64 @test_mid_block(i64 %arg) {
|
|
; CHECK-LABEL: define i64 @test_mid_block(i64 %arg) {
|
|
; CHECK-NEXT: %x = mul i64 %arg, 3
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %add = add i64 %x, %constexpr
|
|
; CHECK-NEXT: ret i64 %add
|
|
%x = mul i64 %arg, 3
|
|
%add = add i64 %x, ptrtoint (ptr @g to i64)
|
|
ret i64 %add
|
|
}
|
|
|
|
define i64 @test_phi_non_critical_edge_block_before(i1 %c) {
|
|
; CHECK-LABEL: define i64 @test_phi_non_critical_edge_block_before(i1 %c) {
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: br i1 %c, label %if, label %join
|
|
; CHECK: if:
|
|
; CHECK-NEXT: br label %phi.constexpr
|
|
; CHECK: phi.constexpr:
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: join:
|
|
; CHECK-NEXT: %phi = phi i64 [ 0, %entry ], [ %constexpr, %phi.constexpr ]
|
|
; CHECK-NEXT: ret i64 %phi
|
|
entry:
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i64 [ 0, %entry ], [ ptrtoint (ptr @g to i64), %if ]
|
|
ret i64 %phi
|
|
}
|
|
|
|
define i64 @test_phi_non_critical_edge_block_after(i1 %c) {
|
|
; CHECK-LABEL: define i64 @test_phi_non_critical_edge_block_after(i1 %c) {
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: br i1 %c, label %if, label %join
|
|
; CHECK: phi.constexpr:
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: join:
|
|
; CHECK-NEXT: %phi = phi i64 [ 0, %entry ], [ %constexpr, %phi.constexpr ]
|
|
; CHECK-NEXT: ret i64 %phi
|
|
; CHECK: if:
|
|
; CHECK-NEXT: br label %phi.constexpr
|
|
entry:
|
|
br i1 %c, label %if, label %join
|
|
|
|
join:
|
|
%phi = phi i64 [ 0, %entry ], [ ptrtoint (ptr @g to i64), %if ]
|
|
ret i64 %phi
|
|
|
|
if:
|
|
br label %join
|
|
}
|
|
|
|
define i64 @test_phi_critical_edge(i1 %c) {
|
|
; CHECK-LABEL: define i64 @test_phi_critical_edge(i1 %c) {
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: br i1 %c, label %if, label %phi.constexpr
|
|
; CHECK: if:
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: phi.constexpr:
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: join:
|
|
; CHECK-NEXT: %phi = phi i64 [ %constexpr, %phi.constexpr ], [ 0, %if ]
|
|
; CHECK-NEXT: ret i64 %phi
|
|
entry:
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i64 [ ptrtoint (ptr @g to i64), %entry ], [ 0, %if ]
|
|
ret i64 %phi
|
|
}
|
|
|
|
define i64 @test_phi_multiple_nodes(i1 %c) {
|
|
; CHECK-LABEL: define i64 @test_phi_multiple_nodes(i1 %c) {
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: br i1 %c, label %if, label %join
|
|
; CHECK: if:
|
|
; CHECK-NEXT: br label %phi.constexpr
|
|
; CHECK: phi.constexpr:
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: %constexpr2 = ptrtoint ptr @g2 to i64
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: join:
|
|
; CHECK-NEXT: %phi = phi i64 [ 0, %entry ], [ %constexpr, %phi.constexpr ]
|
|
; CHECK-NEXT: %phi2 = phi i64 [ 0, %entry ], [ %constexpr2, %phi.constexpr ]
|
|
; CHECK-NEXT: ret i64 %phi
|
|
entry:
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i64 [ 0, %entry ], [ ptrtoint (ptr @g to i64), %if ]
|
|
%phi2 = phi i64 [ 0, %entry ], [ ptrtoint (ptr @g2 to i64), %if ]
|
|
ret i64 %phi
|
|
}
|
|
|
|
|
|
define i64 @test_phi_multiple_identical_predecessors(i32 %x) {
|
|
; CHECK-LABEL: define i64 @test_phi_multiple_identical_predecessors(i32 %x) {
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: switch i32 %x, label %default [
|
|
; CHECK-NEXT: i32 0, label %phi.constexpr
|
|
; CHECK-NEXT: i32 1, label %phi.constexpr
|
|
; CHECK-NEXT: ]
|
|
; CHECK: default:
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: phi.constexpr:
|
|
; CHECK-NEXT: %constexpr = ptrtoint ptr @g to i64
|
|
; CHECK-NEXT: br label %join
|
|
; CHECK: join:
|
|
; CHECK-NEXT: %phi = phi i64 [ %constexpr, %phi.constexpr ], [ %constexpr, %phi.constexpr ], [ 0, %default ]
|
|
; CHECK-NEXT: ret i64 %phi
|
|
entry:
|
|
switch i32 %x, label %default [
|
|
i32 0, label %join
|
|
i32 1, label %join
|
|
]
|
|
|
|
default:
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i64 [ ptrtoint (ptr @g to i64), %entry ], [ ptrtoint (ptr @g to i64), %entry ], [ 0, %default ]
|
|
ret i64 %phi
|
|
}
|