Skip to content

IR generation quality litmus test #6923

Closed
@thestinger

Description

@thestinger

This compiles to the same code post-optimization, so I think it's a good example of how we're turning simple code in very complex IR that LLVM then has to cut down.

Here's a simple C++ program:

#include <cstddef>
#include <utility>

__attribute__((noinline)) void foo(size_t &x, size_t &y) {
    std::swap(x, y);
}

int main() {
    size_t x = 5;
    size_t y = 10;
    foo(x, y);
}

Clang generates this IR at -O0:

; ModuleID = 'foo.cc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define void @_Z3fooRmS_(i64* %x, i64* %y) uwtable noinline {
  %1 = alloca i64*, align 8
  %2 = alloca i64*, align 8
  store i64* %x, i64** %1, align 8
  store i64* %y, i64** %2, align 8
  %3 = load i64** %1, align 8
  %4 = load i64** %2, align 8
  call void @_ZSt4swapImEvRT_S1_(i64* %3, i64* %4)
  ret void
}

define linkonce_odr void @_ZSt4swapImEvRT_S1_(i64* %__a, i64* %__b) nounwind uwtable inlinehint {
  %1 = alloca i64*, align 8
  %2 = alloca i64*, align 8
  %__tmp = alloca i64, align 8
  store i64* %__a, i64** %1, align 8
  store i64* %__b, i64** %2, align 8
  %3 = load i64** %1, align 8
  %4 = load i64* %3, align 8
  store i64 %4, i64* %__tmp, align 8
  %5 = load i64** %2, align 8
  %6 = load i64* %5, align 8
  %7 = load i64** %1, align 8
  store i64 %6, i64* %7, align 8
  %8 = load i64* %__tmp, align 8
  %9 = load i64** %2, align 8
  store i64 %8, i64* %9, align 8
  ret void
}

define i32 @main() uwtable {
  %x = alloca i64, align 8
  %y = alloca i64, align 8
  store i64 5, i64* %x, align 8
  store i64 10, i64* %y, align 8
  call void @_Z3fooRmS_(i64* %x, i64* %y)
  ret i32 0
}

The same thing in Rust:

use std::util;

#[inline(never)]
fn swap(x: &mut int, y: &mut int) {
    util::swap(x, y);
}

fn main() {
    let mut x = 5;
    let mut y = 10;
    swap(&mut x, &mut y);
}

The horror we generate:

; ModuleID = 'foo.rc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%tydesc = type { i64, i64, void ({}*, %tydesc**, i8*)*, void ({}*, %tydesc**, i8*)*, void ({}*, %tydesc**, i8*)*, void ({}*, %tydesc**, i8*)*, i8*, i8* }

@_rust_crate_map_toplevel = global { i32, i8*, i64, [2 x i64] } { i32 1, i8* bitcast ({} ({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*)* @_ZN7cleanup10annihilate17_c17425bfbdabf8296_07preE to i8*), i64 ptrtoint ([1 x { i64, i64 }]* @_rust_mod_map to i64), [2 x i64] [i64 ptrtoint (i64* @_rust_crate_map_std_0.7-pre_c3ca5d77d81b46c1 to i64), i64 0] }
@_rust_crate_map_std_0.7-pre_c3ca5d77d81b46c1 = external global i64
@_rust_mod_map = internal global [1 x { i64, i64 }] zeroinitializer
@rust_abi_version = constant i64 1

declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) #0

define internal {} @_ZN4swap17_a71830ca3ed2d65d3_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*, i64*, i64*) #1 {
static_allocas:
  %3 = alloca {}
  %4 = alloca i64
  %5 = alloca i64
  %6 = alloca {}
  %7 = alloca i64*
  %8 = alloca i64*
  %9 = alloca i64
  %10 = alloca i64*
  %11 = alloca i64*
  %12 = alloca i64
  %13 = alloca {}
  %14 = alloca i64*
  %15 = alloca i64*
  %16 = alloca i64
  %17 = alloca i64*
  %18 = alloca i64*
  %19 = alloca i64
  %20 = alloca {}
  %21 = alloca i64*
  %22 = alloca i64*
  %23 = alloca i64
  %24 = alloca i64*
  %25 = alloca i64*
  %26 = alloca i64
  %27 = alloca {}
  %28 = alloca i64*
  %29 = alloca i64*
  %30 = alloca i64
  %31 = alloca i64*
  %32 = alloca i64*
  %33 = alloca i64*
  %34 = alloca i64
  %35 = alloca i64*
  %36 = alloca i64*
  %37 = alloca i64
  %38 = alloca i64*
  %39 = alloca i64*
  %40 = alloca i64
  %41 = alloca i64
  %42 = alloca {}
  %43 = alloca i64*
  %44 = alloca i64*
  %45 = alloca i64*
  %46 = alloca i64*
  br label %48

return:                                           ; preds = %179
  %47 = load {}* %42
  ret {} %47

; <label>:48                                      ; preds = %static_allocas
  store i64* %1, i64** %43
  store i64* %2, i64** %44
  br label %49

; <label>:49                                      ; preds = %48
  %50 = load i64** %43
  store i64* %50, i64** %45
  %51 = load i64** %45
  %52 = load i64** %44
  store i64* %52, i64** %46
  %53 = load i64** %46
  %54 = bitcast {}* %27 to i8*
  call void @llvm.lifetime.start(i64 0, i8* %54)
  %55 = bitcast i64** %28 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %55)
  %56 = bitcast i64** %29 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %56)
  %57 = bitcast i64* %30 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %57)
  %58 = bitcast i64** %31 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %58)
  %59 = bitcast i64** %32 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %59)
  %60 = bitcast i64** %33 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %60)
  %61 = bitcast i64* %34 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %61)
  %62 = bitcast i64** %35 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %62)
  %63 = bitcast i64** %36 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %63)
  %64 = bitcast i64* %37 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %64)
  %65 = bitcast i64** %38 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %65)
  %66 = bitcast i64** %39 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %66)
  %67 = bitcast i64* %40 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %67)
  %68 = bitcast i64* %41 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %68)
  store i64* %51, i64** %28
  store i64* %53, i64** %29
  %69 = call i64 @_ZN8unstable10intrinsics11uninit_287516_b650e1ca1ea7f553_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* undef)
  store i64 %69, i64* %30
  store i64* %30, i64** %31
  %70 = load i64** %31
  store i64* %70, i64** %32
  %71 = load i64** %32
  %72 = load i64** %28
  store i64* %72, i64** %33
  %73 = load i64** %33
  store i64 1, i64* %34
  %74 = load i64* %34
  %75 = bitcast {}* %20 to i8*
  call void @llvm.lifetime.start(i64 0, i8* %75)
  %76 = bitcast i64** %21 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %76)
  %77 = bitcast i64** %22 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %77)
  %78 = bitcast i64* %23 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %78)
  %79 = bitcast i64** %24 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %79)
  %80 = bitcast i64** %25 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %80)
  %81 = bitcast i64* %26 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %81)
  store i64* %71, i64** %21
  store i64* %73, i64** %22
  store i64 %74, i64* %23
  %82 = load i64** %21
  store i64* %82, i64** %24
  %83 = load i64** %24
  %84 = load i64** %22
  store i64* %84, i64** %25
  %85 = load i64** %25
  %86 = load i64* %23
  store i64 %86, i64* %26
  %87 = load i64* %26
  %88 = call {} @_ZN8unstable10intrinsics13memcpy64_288117_9dac8e5426d6e7233_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* undef, i64* %83, i64* %85, i64 %87)
  %89 = load {}* %20
  %90 = bitcast {}* %20 to i8*
  call void @llvm.lifetime.end(i64 0, i8* %90)
  %91 = bitcast i64** %21 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %91)
  %92 = bitcast i64** %22 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %92)
  %93 = bitcast i64* %23 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %93)
  %94 = bitcast i64** %24 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %94)
  %95 = bitcast i64** %25 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %95)
  %96 = bitcast i64* %26 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %96)
  %97 = load i64** %28
  store i64* %97, i64** %35
  %98 = load i64** %35
  %99 = load i64** %29
  store i64* %99, i64** %36
  %100 = load i64** %36
  store i64 1, i64* %37
  %101 = load i64* %37
  %102 = bitcast {}* %13 to i8*
  call void @llvm.lifetime.start(i64 0, i8* %102)
  %103 = bitcast i64** %14 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %103)
  %104 = bitcast i64** %15 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %104)
  %105 = bitcast i64* %16 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %105)
  %106 = bitcast i64** %17 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %106)
  %107 = bitcast i64** %18 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %107)
  %108 = bitcast i64* %19 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %108)
  store i64* %98, i64** %14
  store i64* %100, i64** %15
  store i64 %101, i64* %16
  %109 = load i64** %14
  store i64* %109, i64** %17
  %110 = load i64** %17
  %111 = load i64** %15
  store i64* %111, i64** %18
  %112 = load i64** %18
  %113 = load i64* %16
  store i64 %113, i64* %19
  %114 = load i64* %19
  %115 = call {} @_ZN8unstable10intrinsics13memcpy64_288117_9dac8e5426d6e7233_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* undef, i64* %110, i64* %112, i64 %114)
  %116 = load {}* %13
  %117 = bitcast {}* %13 to i8*
  call void @llvm.lifetime.end(i64 0, i8* %117)
  %118 = bitcast i64** %14 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %118)
  %119 = bitcast i64** %15 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %119)
  %120 = bitcast i64* %16 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %120)
  %121 = bitcast i64** %17 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %121)
  %122 = bitcast i64** %18 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %122)
  %123 = bitcast i64* %19 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %123)
  %124 = load i64** %29
  store i64* %124, i64** %38
  %125 = load i64** %38
  %126 = load i64** %31
  store i64* %126, i64** %39
  %127 = load i64** %39
  store i64 1, i64* %40
  %128 = load i64* %40
  %129 = bitcast {}* %6 to i8*
  call void @llvm.lifetime.start(i64 0, i8* %129)
  %130 = bitcast i64** %7 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %130)
  %131 = bitcast i64** %8 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %131)
  %132 = bitcast i64* %9 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %132)
  %133 = bitcast i64** %10 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %133)
  %134 = bitcast i64** %11 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %134)
  %135 = bitcast i64* %12 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %135)
  store i64* %125, i64** %7
  store i64* %127, i64** %8
  store i64 %128, i64* %9
  %136 = load i64** %7
  store i64* %136, i64** %10
  %137 = load i64** %10
  %138 = load i64** %8
  store i64* %138, i64** %11
  %139 = load i64** %11
  %140 = load i64* %9
  store i64 %140, i64* %12
  %141 = load i64* %12
  %142 = call {} @_ZN8unstable10intrinsics13memcpy64_288117_9dac8e5426d6e7233_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* undef, i64* %137, i64* %139, i64 %141)
  %143 = load {}* %6
  %144 = bitcast {}* %6 to i8*
  call void @llvm.lifetime.end(i64 0, i8* %144)
  %145 = bitcast i64** %7 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %145)
  %146 = bitcast i64** %8 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %146)
  %147 = bitcast i64* %9 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %147)
  %148 = bitcast i64** %10 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %148)
  %149 = bitcast i64** %11 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %149)
  %150 = bitcast i64* %12 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %150)
  %151 = load i64* %30
  store i64 %151, i64* %41
  %152 = load i64* %41
  %153 = bitcast {}* %3 to i8*
  call void @llvm.lifetime.start(i64 0, i8* %153)
  %154 = bitcast i64* %4 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %154)
  %155 = bitcast i64* %5 to i8*
  call void @llvm.lifetime.start(i64 8, i8* %155)
  store i64 %152, i64* %4
  %156 = load i64* %4
  store i64 %156, i64* %5
  %157 = load i64* %5
  %158 = call {} @_ZN8unstable10intrinsics11forget_288717_9475c65f257b4c1e3_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* undef, i64 %157)
  %159 = load {}* %3
  %160 = bitcast {}* %3 to i8*
  call void @llvm.lifetime.end(i64 0, i8* %160)
  %161 = bitcast i64* %4 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %161)
  %162 = bitcast i64* %5 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %162)
  %163 = load {}* %27
  %164 = bitcast {}* %27 to i8*
  call void @llvm.lifetime.end(i64 0, i8* %164)
  %165 = bitcast i64** %28 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %165)
  %166 = bitcast i64** %29 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %166)
  %167 = bitcast i64* %30 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %167)
  %168 = bitcast i64** %31 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %168)
  %169 = bitcast i64** %32 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %169)
  %170 = bitcast i64** %33 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %170)
  %171 = bitcast i64* %34 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %171)
  %172 = bitcast i64** %35 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %172)
  %173 = bitcast i64** %36 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %173)
  %174 = bitcast i64* %37 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %174)
  %175 = bitcast i64** %38 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %175)
  %176 = bitcast i64** %39 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %176)
  %177 = bitcast i64* %40 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %177)
  %178 = bitcast i64* %41 to i8*
  call void @llvm.lifetime.end(i64 8, i8* %178)
  br label %179

; <label>:179                                     ; preds = %49
  br label %return
}

define internal i64 @_ZN8unstable10intrinsics11uninit_287516_b650e1ca1ea7f553_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*) {
static_allocas:
  %1 = alloca i64
  br label %3

return:                                           ; preds = %3
  %2 = load i64* %1
  ret i64 %2

; <label>:3                                       ; preds = %static_allocas
  br label %return
}

define internal {} @_ZN8unstable10intrinsics13memcpy64_288117_9dac8e5426d6e7233_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*, i64*, i64*, i64) {
static_allocas:
  %4 = alloca {}
  br label %6

return:                                           ; preds = %6
  %5 = load {}* %4
  ret {} %5

; <label>:6                                       ; preds = %static_allocas
  %7 = bitcast i64* %1 to i8*
  %8 = bitcast i64* %2 to i8*
  %9 = mul i64 8, %3
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %7, i8* %8, i64 %9, i32 8, i1 false)
  br label %return
}

define internal {} @_ZN8unstable10intrinsics11forget_288717_9475c65f257b4c1e3_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*, i64) {
static_allocas:
  %2 = alloca {}
  br label %4

return:                                           ; preds = %4
  %3 = load {}* %2
  ret {} %3

; <label>:4                                       ; preds = %static_allocas
  br label %return
}

define internal {} @_ZN4main17_3ab48cd538faebd53_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*) #2 {
static_allocas:
  %1 = alloca {}
  %2 = alloca i64
  %3 = alloca i64
  %4 = alloca i64*
  %5 = alloca i64*
  br label %7

return:                                           ; preds = %12
  %6 = load {}* %1
  ret {} %6

; <label>:7                                       ; preds = %static_allocas
  store i64 5, i64* %2
  store i64 10, i64* %3
  br label %8

; <label>:8                                       ; preds = %7
  store i64* %2, i64** %4
  %9 = load i64** %4
  store i64* %3, i64** %5
  %10 = load i64** %5
  %11 = call {} @_ZN4swap17_a71830ca3ed2d65d3_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* undef, i64* %9, i64* %10)
  br label %12

; <label>:12                                      ; preds = %8
  br label %return
}

define {} @_rust_main({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*) {
static_allocas:
  %1 = alloca {}
  br label %3

return:                                           ; preds = %3
  %2 = load {}* %1
  ret {} %2

; <label>:3                                       ; preds = %static_allocas
  %4 = call {} @_ZN4main17_3ab48cd538faebd53_00E({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* %0)
  store {} %4, {}* %1
  br label %return
}

define i64 @main(i64, i8**) {
top:
  %2 = call i64 @_ZN8unstable4lang5start17_80798dc0c0e380dc6_07preE({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)* null, i8* bitcast ({} ({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*)* @_rust_main to i8*), i64 %0, i8** %1, i8* bitcast ({ i32, i8*, i64, [2 x i64] }* @_rust_crate_map_toplevel to i8*))
  ret i64 %2
}

declare i64 @_ZN8unstable4lang5start17_80798dc0c0e380dc6_07preE({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*, i8*, i64, i8**, i8*)

declare {} @_ZN7cleanup10annihilate17_c17425bfbdabf8296_07preE({ i64, %tydesc*, i8*, i8*, i8 } addrspace(1)*)

declare void @llvm.lifetime.start(i64, i8* nocapture) #0

declare void @llvm.lifetime.end(i64, i8* nocapture) #0

attributes #0 = { nounwind }
attributes #1 = { noinline uwtable }
attributes #2 = { uwtable }

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-codegenArea: Code generationI-slowIssue: Problems and improvements with respect to performance of generated code.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions