; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -S -passes="default" < %s | FileCheck %s ; Test that the CGSCC inliner forwards stores to load arguments after ; inlining. This addresses a phase ordering issue: the constructor is ; inlined first (producing stores), and then the destructor's load ; arguments should see the stored constants. Without this forwarding, ; the recursive erase function appears too expensive to inline. ; ; This models the std::set{} pattern where the constructor stores ; null to the root pointer and the destructor checks it before recursive ; tree deletion (_Rb_tree::_M_erase). target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" declare void @free(ptr) declare void @use(i64) ; Models _Rb_tree constructor: stores null root + zero node count. define internal void @ctor(ptr %this) { store ptr null, ptr %this, align 8 %count = getelementptr inbounds i8, ptr %this, i64 8 store i64 0, ptr %count, align 8 ret void } ; Models ~_Rb_tree: loads root and calls the recursive eraser. define internal void @dtor(ptr %this) { %root = load ptr, ptr %this, align 8 call void @erase(ptr %root) ret void } ; Models _Rb_tree::_M_erase — recursive node deletion. ; With null: icmp + branch to done → trivially cheap. ; With unknown ptr: recursive calls + external calls → too expensive. define internal void @erase(ptr %node) { %is_null = icmp eq ptr %node, null br i1 %is_null, label %done, label %recurse recurse: %left_val = load ptr, ptr %node, align 8 call void @erase(ptr %left_val) %right_ptr = getelementptr inbounds i8, ptr %node, i64 8 %right_val = load ptr, ptr %right_ptr, align 8 call void @erase(ptr %right_val) %d1 = getelementptr inbounds i8, ptr %node, i64 16 %v1 = load i64, ptr %d1, align 8 call void @use(i64 %v1) %d2 = getelementptr inbounds i8, ptr %node, i64 24 %v2 = load i64, ptr %d2, align 8 call void @use(i64 %v2) %d3 = getelementptr inbounds i8, ptr %node, i64 32 %v3 = load i64, ptr %d3, align 8 call void @use(i64 %v3) %d4 = getelementptr inbounds i8, ptr %node, i64 40 %v4 = load i64, ptr %d4, align 8 call void @use(i64 %v4) %d5 = getelementptr inbounds i8, ptr %node, i64 48 %v5 = load i64, ptr %d5, align 8 call void @use(i64 %v5) call void @free(ptr %node) br label %done done: ret void } define void @test_empty_tree() { ; CHECK-LABEL: define void @test_empty_tree( ; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: ret void ; %tree = alloca ptr, align 8 call void @ctor(ptr %tree) call void @dtor(ptr %tree) ret void }