104 changes: 81 additions & 23 deletions clang/test/CodeGenHLSL/builtins/mad.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -64,59 +64,107 @@ int16_t3 test_mad_int16_t3(int16_t3 p0, int16_t3 p1, int16_t3 p2) { return mad(p
int16_t4 test_mad_int16_t4(int16_t4 p0, int16_t4 p1, int16_t4 p2) { return mad(p0, p1, p2); }
#endif // __HLSL_ENABLE_16_BIT

// NATIVE_HALF: %hlsl.fmad = call half @llvm.fmuladd.f16(half %0, half %1, half %2)
// NATIVE_HALF: %[[p0:.*]] = load half, ptr %p0.addr, align 2
// NATIVE_HALF: %[[p1:.*]] = load half, ptr %p1.addr, align 2
// NATIVE_HALF: %[[p2:.*]] = load half, ptr %p2.addr, align 2
// NATIVE_HALF: %hlsl.fmad = call half @llvm.fmuladd.f16(half %[[p0]], half %[[p1]], half %[[p2]])
// NATIVE_HALF: ret half %hlsl.fmad
// NO_HALF: %hlsl.fmad = call float @llvm.fmuladd.f32(float %0, float %1, float %2)
// NO_HALF: %[[p0:.*]] = load float, ptr %p0.addr, align 4
// NO_HALF: %[[p1:.*]] = load float, ptr %p1.addr, align 4
// NO_HALF: %[[p2:.*]] = load float, ptr %p2.addr, align 4
// NO_HALF: %hlsl.fmad = call float @llvm.fmuladd.f32(float %[[p0]], float %[[p1]], float %[[p2]])
// NO_HALF: ret float %hlsl.fmad
half test_mad_half(half p0, half p1, half p2) { return mad(p0, p1, p2); }

// NATIVE_HALF: %hlsl.fmad = call <2 x half> @llvm.fmuladd.v2f16(<2 x half> %0, <2 x half> %1, <2 x half> %2)
// NATIVE_HALF: %[[p0:.*]] = load <2 x half>, ptr %p0.addr, align 4
// NATIVE_HALF: %[[p1:.*]] = load <2 x half>, ptr %p1.addr, align 4
// NATIVE_HALF: %[[p2:.*]] = load <2 x half>, ptr %p2.addr, align 4
// NATIVE_HALF: %hlsl.fmad = call <2 x half> @llvm.fmuladd.v2f16(<2 x half> %[[p0]], <2 x half> %[[p1]], <2 x half> %[[p2]])
// NATIVE_HALF: ret <2 x half> %hlsl.fmad
// NO_HALF: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %2)
// NO_HALF: %[[p0:.*]] = load <2 x float>, ptr %p0.addr, align 8
// NO_HALF: %[[p1:.*]] = load <2 x float>, ptr %p1.addr, align 8
// NO_HALF: %[[p2:.*]] = load <2 x float>, ptr %p2.addr, align 8
// NO_HALF: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %[[p0]], <2 x float> %[[p1]], <2 x float> %[[p2]])
// NO_HALF: ret <2 x float> %hlsl.fmad
half2 test_mad_half2(half2 p0, half2 p1, half2 p2) { return mad(p0, p1, p2); }

// NATIVE_HALF: %hlsl.fmad = call <3 x half> @llvm.fmuladd.v3f16(<3 x half> %0, <3 x half> %1, <3 x half> %2)
// NATIVE_HALF: %[[p0:.*]] = load <3 x half>, ptr %p0.addr, align 8
// NATIVE_HALF: %[[p1:.*]] = load <3 x half>, ptr %p1.addr, align 8
// NATIVE_HALF: %[[p2:.*]] = load <3 x half>, ptr %p2.addr, align 8
// NATIVE_HALF: %hlsl.fmad = call <3 x half> @llvm.fmuladd.v3f16(<3 x half> %[[p0]], <3 x half> %[[p1]], <3 x half> %[[p2]])
// NATIVE_HALF: ret <3 x half> %hlsl.fmad
// NO_HALF: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %0, <3 x float> %1, <3 x float> %2)
// NO_HALF: %[[p0:.*]] = load <3 x float>, ptr %p0.addr, align 16
// NO_HALF: %[[p1:.*]] = load <3 x float>, ptr %p1.addr, align 16
// NO_HALF: %[[p2:.*]] = load <3 x float>, ptr %p2.addr, align 16
// NO_HALF: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %[[p0]], <3 x float> %[[p1]], <3 x float> %[[p2]])
// NO_HALF: ret <3 x float> %hlsl.fmad
half3 test_mad_half3(half3 p0, half3 p1, half3 p2) { return mad(p0, p1, p2); }

// NATIVE_HALF: %hlsl.fmad = call <4 x half> @llvm.fmuladd.v4f16(<4 x half> %0, <4 x half> %1, <4 x half> %2)
// NATIVE_HALF: %[[p0:.*]] = load <4 x half>, ptr %p0.addr, align 8
// NATIVE_HALF: %[[p1:.*]] = load <4 x half>, ptr %p1.addr, align 8
// NATIVE_HALF: %[[p2:.*]] = load <4 x half>, ptr %p2.addr, align 8
// NATIVE_HALF: %hlsl.fmad = call <4 x half> @llvm.fmuladd.v4f16(<4 x half> %[[p0]], <4 x half> %[[p1]], <4 x half> %[[p2]])
// NATIVE_HALF: ret <4 x half> %hlsl.fmad
// NO_HALF: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %0, <4 x float> %1, <4 x float> %2)
// NO_HALF: %[[p0:.*]] = load <4 x float>, ptr %p0.addr, align 16
// NO_HALF: %[[p1:.*]] = load <4 x float>, ptr %p1.addr, align 16
// NO_HALF: %[[p2:.*]] = load <4 x float>, ptr %p2.addr, align 16
// NO_HALF: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %[[p0]], <4 x float> %[[p1]], <4 x float> %[[p2]])
// NO_HALF: ret <4 x float> %hlsl.fmad
half4 test_mad_half4(half4 p0, half4 p1, half4 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call float @llvm.fmuladd.f32(float %0, float %1, float %2)
// CHECK: %[[p0:.*]] = load float, ptr %p0.addr, align 4
// CHECK: %[[p1:.*]] = load float, ptr %p1.addr, align 4
// CHECK: %[[p2:.*]] = load float, ptr %p2.addr, align 4
// CHECK: %hlsl.fmad = call float @llvm.fmuladd.f32(float %[[p0]], float %[[p1]], float %[[p2]])
// CHECK: ret float %hlsl.fmad
float test_mad_float(float p0, float p1, float p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %2)
// CHECK: %[[p0:.*]] = load <2 x float>, ptr %p0.addr, align 8
// CHECK: %[[p1:.*]] = load <2 x float>, ptr %p1.addr, align 8
// CHECK: %[[p2:.*]] = load <2 x float>, ptr %p2.addr, align 8
// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %[[p0]], <2 x float> %[[p1]], <2 x float> %[[p2]])
// CHECK: ret <2 x float> %hlsl.fmad
float2 test_mad_float2(float2 p0, float2 p1, float2 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %0, <3 x float> %1, <3 x float> %2)
// CHECK: %[[p0:.*]] = load <3 x float>, ptr %p0.addr, align 16
// CHECK: %[[p1:.*]] = load <3 x float>, ptr %p1.addr, align 16
// CHECK: %[[p2:.*]] = load <3 x float>, ptr %p2.addr, align 16
// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %[[p0]], <3 x float> %[[p1]], <3 x float> %[[p2]])
// CHECK: ret <3 x float> %hlsl.fmad
float3 test_mad_float3(float3 p0, float3 p1, float3 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %0, <4 x float> %1, <4 x float> %2)
// CHECK: %[[p0:.*]] = load <4 x float>, ptr %p0.addr, align 16
// CHECK: %[[p1:.*]] = load <4 x float>, ptr %p1.addr, align 16
// CHECK: %[[p2:.*]] = load <4 x float>, ptr %p2.addr, align 16
// CHECK: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %[[p0]], <4 x float> %[[p1]], <4 x float> %[[p2]])
// CHECK: ret <4 x float> %hlsl.fmad
float4 test_mad_float4(float4 p0, float4 p1, float4 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call double @llvm.fmuladd.f64(double %0, double %1, double %2)
// CHECK: %[[p0:.*]] = load double, ptr %p0.addr, align 8
// CHECK: %[[p1:.*]] = load double, ptr %p1.addr, align 8
// CHECK: %[[p2:.*]] = load double, ptr %p2.addr, align 8
// CHECK: %hlsl.fmad = call double @llvm.fmuladd.f64(double %[[p0]], double %[[p1]], double %[[p2]])
// CHECK: ret double %hlsl.fmad
double test_mad_double(double p0, double p1, double p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <2 x double> @llvm.fmuladd.v2f64(<2 x double> %0, <2 x double> %1, <2 x double> %2)
// CHECK: %[[p0:.*]] = load <2 x double>, ptr %p0.addr, align 16
// CHECK: %[[p1:.*]] = load <2 x double>, ptr %p1.addr, align 16
// CHECK: %[[p2:.*]] = load <2 x double>, ptr %p2.addr, align 16
// CHECK: %hlsl.fmad = call <2 x double> @llvm.fmuladd.v2f64(<2 x double> %[[p0]], <2 x double> %[[p1]], <2 x double> %[[p2]])
// CHECK: ret <2 x double> %hlsl.fmad
double2 test_mad_double2(double2 p0, double2 p1, double2 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <3 x double> @llvm.fmuladd.v3f64(<3 x double> %0, <3 x double> %1, <3 x double> %2)
// CHECK: %[[p0:.*]] = load <3 x double>, ptr %p0.addr, align 32
// CHECK: %[[p1:.*]] = load <3 x double>, ptr %p1.addr, align 32
// CHECK: %[[p2:.*]] = load <3 x double>, ptr %p2.addr, align 32
// CHECK: %hlsl.fmad = call <3 x double> @llvm.fmuladd.v3f64(<3 x double> %[[p0]], <3 x double> %[[p1]], <3 x double> %[[p2]])
// CHECK: ret <3 x double> %hlsl.fmad
double3 test_mad_double3(double3 p0, double3 p1, double3 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %0, <4 x double> %1, <4 x double> %2)
// CHECK: %[[p0:.*]] = load <4 x double>, ptr %p0.addr, align 32
// CHECK: %[[p1:.*]] = load <4 x double>, ptr %p1.addr, align 32
// CHECK: %[[p2:.*]] = load <4 x double>, ptr %p2.addr, align 32
// CHECK: %hlsl.fmad = call <4 x double> @llvm.fmuladd.v4f64(<4 x double> %[[p0]], <4 x double> %[[p1]], <4 x double> %[[p2]])
// CHECK: ret <4 x double> %hlsl.fmad
double4 test_mad_double4(double4 p0, double4 p1, double4 p2) { return mad(p0, p1, p2); }

Expand Down Expand Up @@ -216,31 +264,41 @@ uint64_t3 test_mad_uint64_t3(uint64_t3 p0, uint64_t3 p1, uint64_t3 p2) { return
// SPIR_CHECK: add nuw <4 x i64> %{{.*}}, %{{.*}}
uint64_t4 test_mad_uint64_t4(uint64_t4 p0, uint64_t4 p1, uint64_t4 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %splat.splat, <2 x float> %1, <2 x float> %2)
// CHECK: %[[p1:.*]] = load <2 x float>, ptr %p1.addr, align 8
// CHECK: %[[p2:.*]] = load <2 x float>, ptr %p2.addr, align 8
// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %splat.splat, <2 x float> %[[p1]], <2 x float> %[[p2]])
// CHECK: ret <2 x float> %hlsl.fmad
float2 test_mad_float2_splat(float p0, float2 p1, float2 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %splat.splat, <3 x float> %1, <3 x float> %2)
// CHECK: %[[p1:.*]] = load <3 x float>, ptr %p1.addr, align 16
// CHECK: %[[p2:.*]] = load <3 x float>, ptr %p2.addr, align 16
// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %splat.splat, <3 x float> %[[p1]], <3 x float> %[[p2]])
// CHECK: ret <3 x float> %hlsl.fmad
float3 test_mad_float3_splat(float p0, float3 p1, float3 p2) { return mad(p0, p1, p2); }

// CHECK: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %splat.splat, <4 x float> %1, <4 x float> %2)
// CHECK: %[[p1:.*]] = load <4 x float>, ptr %p1.addr, align 16
// CHECK: %[[p2:.*]] = load <4 x float>, ptr %p2.addr, align 16
// CHECK: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %splat.splat, <4 x float> %[[p1]], <4 x float> %[[p2]])
// CHECK: ret <4 x float> %hlsl.fmad
float4 test_mad_float4_splat(float p0, float4 p1, float4 p2) { return mad(p0, p1, p2); }

// CHECK: %conv = sitofp i32 %2 to float
// CHECK: %[[p0:.*]] = load <2 x float>, ptr %p0.addr, align 8
// CHECK: %[[p1:.*]] = load <2 x float>, ptr %p1.addr, align 8
// CHECK: %conv = sitofp i32 %{{.*}} to float
// CHECK: %splat.splatinsert = insertelement <2 x float> poison, float %conv, i64 0
// CHECK: %splat.splat = shufflevector <2 x float> %splat.splatinsert, <2 x float> poison, <2 x i32> zeroinitializer
// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %splat.splat)
// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %[[p0]], <2 x float> %[[p1]], <2 x float> %splat.splat)
// CHECK: ret <2 x float> %hlsl.fmad
float2 test_mad_float2_int_splat(float2 p0, float2 p1, int p2) {
return mad(p0, p1, p2);
}

// CHECK: %conv = sitofp i32 %2 to float
// CHECK: %[[p0:.*]] = load <3 x float>, ptr %p0.addr, align 16
// CHECK: %[[p1:.*]] = load <3 x float>, ptr %p1.addr, align 16
// CHECK: %conv = sitofp i32 %{{.*}} to float
// CHECK: %splat.splatinsert = insertelement <3 x float> poison, float %conv, i64 0
// CHECK: %splat.splat = shufflevector <3 x float> %splat.splatinsert, <3 x float> poison, <3 x i32> zeroinitializer
// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %0, <3 x float> %1, <3 x float> %splat.splat)
// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %[[p0]], <3 x float> %[[p1]], <3 x float> %splat.splat)
// CHECK: ret <3 x float> %hlsl.fmad
float3 test_mad_float3_int_splat(float3 p0, float3 p1, int p2) {
return mad(p0, p1, p2);
Expand Down
90 changes: 90 additions & 0 deletions clang/test/CodeGenHLSL/convergence/do.while.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s

bool cond();
void foo();

void test1() {
do {
} while (cond());
}
// CHECK: define spir_func void @_Z5test1v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: do.body:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: do.cond:
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test2() {
do {
foo();
} while (cond());
}
// CHECK: define spir_func void @_Z5test2v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: do.body:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: do.cond:
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test3() {
do {
if (cond())
foo();
} while (cond());
}
// CHECK: define spir_func void @_Z5test3v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: do.body:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: if.then:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: do.cond:
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test4() {
do {
if (cond()) {
foo();
break;
}
} while (cond());
}
// CHECK: define spir_func void @_Z5test4v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: do.body:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: if.then:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: do.cond:
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test5() {
do {
while (cond()) {
if (cond()) {
foo();
break;
}
}
} while (cond());
}
// CHECK: define spir_func void @_Z5test5v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: do.body:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: while.cond:
// CHECK: [[T2:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T1]]) ]
// CHECK: if.then:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T2]]) ]
// CHECK: do.cond:
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

// CHECK-DAG: attributes [[A0]] = { {{.*}}convergent{{.*}} }
// CHECK-DAG: attributes [[A3]] = { {{.*}}convergent{{.*}} }
121 changes: 121 additions & 0 deletions clang/test/CodeGenHLSL/convergence/for.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s

bool cond();
bool cond2();
void foo();

void test1() {
for (;;) {
foo();
}
}
// CHECK: define spir_func void @_Z5test1v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test2() {
for (;cond();) {
foo();
}
}
// CHECK: define spir_func void @_Z5test2v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: for.body:
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test3() {
for (cond();;) {
foo();
}
}
// CHECK: define spir_func void @_Z5test3v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T0]]) ]
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test4() {
for (cond();cond2();) {
foo();
}
}
// CHECK: define spir_func void @_Z5test4v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T0]]) ]
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z5cond2v() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: for.body:
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test5() {
for (cond();cond2();foo()) {
}
}
// CHECK: define spir_func void @_Z5test5v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T0]]) ]
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z5cond2v() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: for.inc:
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test6() {
for (cond();cond2();foo()) {
if (cond()) {
foo();
break;
}
}
}
// CHECK: define spir_func void @_Z5test6v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T0]]) ]
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z5cond2v() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: for.body:
// CHECK: [[C1:%[a-zA-Z0-9]+]] = call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: br i1 [[C1]], label %if.then, label %if.end
// CHECK: if.then:
// CHECK call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: br label %for.end
// CHECK: if.end:
// CHECK: br label %for.inc
// CHECK: for.inc:
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test7() {
for (cond();;) {
for (cond();;) {
foo();
}
}
}
// CHECK: define spir_func void @_Z5test7v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T0]]) ]
// CHECK: for.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: for.cond3:
// CHECK: [[T2:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T1]]) ]
// CHECK: call spir_func void @_Z3foov() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T2]]) ]

// CHECK-DAG: attributes [[A0]] = { {{.*}}convergent{{.*}} }
// CHECK-DAG: attributes [[A3]] = { {{.*}}convergent{{.*}} }
119 changes: 119 additions & 0 deletions clang/test/CodeGenHLSL/convergence/while.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s

bool cond();
void foo();

void test1() {
while (cond()) {
}
}
// CHECK: define spir_func void @_Z5test1v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: while.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3:#[0-9]+]] [ "convergencectrl"(token [[T1]]) ]

void test2() {
while (cond()) {
foo();
}
}
// CHECK: define spir_func void @_Z5test2v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: while.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: while.body:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]

void test3() {
while (cond()) {
if (cond())
break;
foo();
}
}
// CHECK: define spir_func void @_Z5test3v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: while.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: if.then:
// CHECK: br label %while.end
// CHECK: if.end:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: br label %while.cond

void test4() {
while (cond()) {
if (cond()) {
foo();
break;
}
}
}
// CHECK: define spir_func void @_Z5test4v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: while.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: if.then:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: br label %while.end
// CHECK: if.end:
// CHECK: br label %while.cond

void test5() {
while (cond()) {
while (cond()) {
if (cond()) {
foo();
break;
}
}
}
}
// CHECK: define spir_func void @_Z5test5v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: while.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: while.cond2:
// CHECK: [[T2:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T1]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T2]]) ]
// CHECK: if.then:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T2]]) ]
// CHECK: br label %while.end

void test6() {
while (cond()) {
while (cond()) {
}

if (cond()) {
foo();
break;
}
}
}
// CHECK: define spir_func void @_Z5test6v() [[A0:#[0-9]+]] {
// CHECK: entry:
// CHECK: [[T0:%[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: while.cond:
// CHECK: [[T1:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T0]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: while.cond2:
// CHECK: [[T2:%[0-9]+]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[T1]]) ]
// CHECK: call spir_func noundef i1 @_Z4condv() [[A3]] [ "convergencectrl"(token [[T2]]) ]
// CHECK: if.then:
// CHECK: call spir_func void @_Z3foov() [[A3]] [ "convergencectrl"(token [[T1]]) ]
// CHECK: br label %while.end

// CHECK-DAG: attributes [[A0]] = { {{.*}}convergent{{.*}} }
// CHECK-DAG: attributes [[A3]] = { {{.*}}convergent{{.*}} }
4 changes: 2 additions & 2 deletions clang/test/Frontend/noderef_templates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#define NODEREF __attribute__((noderef))

template <typename T>
int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
int func(T NODEREF *a) { // expected-note 3 {{a declared here}}
return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}}
}

void func() {
Expand Down
6 changes: 2 additions & 4 deletions clang/test/SemaCXX/cxx2b-deducing-this.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct S {
// new and delete are implicitly static
void *operator new(this unsigned long); // expected-error{{an explicit object parameter cannot appear in a static function}}
void operator delete(this void*); // expected-error{{an explicit object parameter cannot appear in a static function}}

void g(this auto) const; // expected-error{{explicit object member function cannot have 'const' qualifier}}
void h(this auto) &; // expected-error{{explicit object member function cannot have '&' qualifier}}
void i(this auto) &&; // expected-error{{explicit object member function cannot have '&&' qualifier}}
Expand Down Expand Up @@ -198,9 +198,7 @@ void func(int i) {
void TestMutationInLambda() {
[i = 0](this auto &&){ i++; }();
[i = 0](this auto){ i++; }();
[i = 0](this const auto&){ i++; }();
// expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
// expected-note@-2 {{in instantiation of}}
[i = 0](this const auto&){ i++; }(); // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}

int x;
const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
Expand Down
20 changes: 20 additions & 0 deletions clang/test/SemaCXX/cxx2c-attributes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-pc-linux -fsyntax-only -verify -Wno-c++17-extensions
// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-windows-msvc -fsyntax-only -verify=msvc -Wno-c++17-extensions
// expected-no-diagnostics

// Check we return non-zero values for supported attributes as per
// wg21.link/P2552
static_assert(__has_cpp_attribute(assume));

// The standard does not prescribe a behavior for [[carries_dependency]]

static_assert(__has_cpp_attribute(deprecated));
static_assert(__has_cpp_attribute(fallthrough));
static_assert(__has_cpp_attribute(likely));
static_assert(__has_cpp_attribute(unlikely));
static_assert(__has_cpp_attribute(maybe_unused));
static_assert(__has_cpp_attribute(nodiscard));
static_assert(__has_cpp_attribute(noreturn));

// We do not support [[no_unique_address]] in MSVC emulation mode
static_assert(__has_cpp_attribute(no_unique_address)); // msvc-error {{static assertion failed}}
23 changes: 23 additions & 0 deletions clang/test/SemaCXX/cxx2c-pack-indexing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,26 @@ void h() {
// expected-note-re@-2 {{function template specialization '{{.*}}' requested here}}
}
}

namespace GH91885 {

void test(auto...args){
[&]<int idx>(){
using R = decltype( args...[idx] ) ;
}.template operator()<0>();
}

template<int... args>
void test2(){
[&]<int idx>(){
using R = decltype( args...[idx] ) ;
}.template operator()<0>();
}

void f( ) {
test(1);
test2<1>();
}


}
12 changes: 6 additions & 6 deletions clang/test/SemaTemplate/class-template-spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int test_specs(A<float, float> *a1, A<float, int> *a2) {
return a1->x + a2->y;
}

int test_incomplete_specs(A<double, double> *a1,
int test_incomplete_specs(A<double, double> *a1,
A<double> *a2)
{
(void)a1->x; // expected-error{{member access into incomplete type}}
Expand All @@ -39,7 +39,7 @@ template <> struct X<int, int> { int foo(); }; // #1
template <> struct X<float> { int bar(); }; // #2

typedef int int_type;
void testme(X<int_type> *x1, X<float, int> *x2) {
void testme(X<int_type> *x1, X<float, int> *x2) {
(void)x1->foo(); // okay: refers to #1
(void)x2->bar(); // okay: refers to #2
}
Expand All @@ -53,7 +53,7 @@ struct A<char> {
A<char>::A() { }

// Make sure we can see specializations defined before the primary template.
namespace N{
namespace N{
template<typename T> struct A0;
}

Expand Down Expand Up @@ -97,7 +97,7 @@ namespace M {
template<> struct ::A<long double>; // expected-error{{must occur at global scope}}
}

template<> struct N::B<char> {
template<> struct N::B<char> {
int testf(int x) { return f(x); }
};

Expand Down Expand Up @@ -138,9 +138,9 @@ namespace PR18009 {

template <typename T> struct C {
template <int N, int M> struct S;
template <int N> struct S<N, N ? **(T(*)[N])0 : 0> {}; // expected-error {{depends on a template parameter of the partial specialization}}
template <int N> struct S<N, N ? **(T(*)[N])0 : 0> {}; // ok
};
C<int> c; // expected-note {{in instantiation of}}
C<int> c;

template<int A> struct outer {
template<int B, int C> struct inner {};
Expand Down
6 changes: 3 additions & 3 deletions clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1572,9 +1572,9 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) {
EXPECT_TRUE(
matches("template <class T> class Y { void x() { this->m; } int m; };",
memberExpr(isArrow())));
EXPECT_TRUE(
notMatches("template <class T> class Y { void x() { (*this).m; } };",
cxxDependentScopeMemberExpr(isArrow())));
EXPECT_TRUE(notMatches(
"template <class T> class Y { void x() { (*this).m; } int m; };",
memberExpr(isArrow())));
}

TEST_P(ASTMatchersTest, IsArrow_MatchesStaticMemberVariablesViaArrow) {
Expand Down
35 changes: 35 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
#include <string>
#include <utility>

namespace clang {
namespace dataflow {
namespace {
AST_MATCHER(FunctionDecl, isTemplated) { return Node.isTemplated(); }
} // namespace
} // namespace dataflow
} // namespace clang

namespace {

using namespace clang;
Expand Down Expand Up @@ -7416,4 +7424,31 @@ TEST(TransferTest, ConditionalRelation) {
});
}

// This is a crash repro.
// We used to crash while transferring `S().i` because Clang contained a bug
// causing the AST to be malformed.
TEST(TransferTest, AnonymousUnionMemberExprInTemplate) {
using ast_matchers::functionDecl;
using ast_matchers::hasName;
using ast_matchers::unless;

std::string Code = R"cc(
struct S {
struct {
int i;
};
};
template <class>
void target() {
S().i;
}
template void target<int>();
)cc";
auto Matcher = functionDecl(hasName("target"), unless(isTemplated()));
ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code, Matcher),
llvm::Succeeded());
}

} // namespace
111 changes: 54 additions & 57 deletions clang/unittests/Format/QualifierFixerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1055,81 +1055,78 @@ TEST_F(QualifierFixerTest, IsQualifierType) {
ConfiguredTokens.push_back(tok::kw_constexpr);
ConfiguredTokens.push_back(tok::kw_friend);

auto Tokens = annotate(
TestLexer lexer{Allocator, Buffers};
const auto LangOpts = getFormattingLangOpts();

auto Tokens = lexer.lex(
"const static inline auto restrict int double long constexpr friend");
ASSERT_EQ(Tokens.size(), 11u) << Tokens;

EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[0], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[1], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[2], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[3], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[4], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[5], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[6], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[7], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[8], ConfiguredTokens));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
Tokens[9], ConfiguredTokens));

EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[0]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[1]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[2]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[3]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[4]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[5]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[6]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[7]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[8]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(Tokens[9]));

auto NotTokens = annotate("for while do Foo Bar ");
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[0], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[1], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[2], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[3], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[4], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[5], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[6], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[7], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[8], ConfiguredTokens, LangOpts));
EXPECT_TRUE(
isConfiguredQualifierOrType(Tokens[9], ConfiguredTokens, LangOpts));

EXPECT_TRUE(isQualifierOrType(Tokens[0], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[1], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[2], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[3], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[4], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[5], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[6], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[7], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[8], LangOpts));
EXPECT_TRUE(isQualifierOrType(Tokens[9], LangOpts));

auto NotTokens = lexer.lex("for while do Foo Bar ");
ASSERT_EQ(NotTokens.size(), 6u) << Tokens;

EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[0], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[1], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[2], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[3], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[4], ConfiguredTokens));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
NotTokens[5], ConfiguredTokens));

EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[0]));
isConfiguredQualifierOrType(NotTokens[0], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[1]));
isConfiguredQualifierOrType(NotTokens[1], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[2]));
isConfiguredQualifierOrType(NotTokens[2], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[3]));
isConfiguredQualifierOrType(NotTokens[3], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[4]));
isConfiguredQualifierOrType(NotTokens[4], ConfiguredTokens, LangOpts));
EXPECT_FALSE(
LeftRightQualifierAlignmentFixer::isQualifierOrType(NotTokens[5]));
isConfiguredQualifierOrType(NotTokens[5], ConfiguredTokens, LangOpts));

EXPECT_FALSE(isQualifierOrType(NotTokens[0], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[1], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[2], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[3], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[4], LangOpts));
EXPECT_FALSE(isQualifierOrType(NotTokens[5], LangOpts));
}

TEST_F(QualifierFixerTest, IsMacro) {

auto Tokens = annotate("INT INTPR Foo int");
ASSERT_EQ(Tokens.size(), 5u) << Tokens;

EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[0]));
EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[1]));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[2]));
EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[3]));
EXPECT_TRUE(isPossibleMacro(Tokens[0]));
EXPECT_TRUE(isPossibleMacro(Tokens[1]));
EXPECT_FALSE(isPossibleMacro(Tokens[2]));
EXPECT_FALSE(isPossibleMacro(Tokens[3]));
}

TEST_F(QualifierFixerTest, OverlappingQualifier) {
Expand Down
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td>On the ignorability of standard attributes</td>
<td><a href="https://wg21.link/P2552R3">P2552R3</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
<td class="full" align="center">Yes</td>
</tr>
<tr>
<td>Static storage for braced initializers</td>
Expand Down
22 changes: 19 additions & 3 deletions compiler-rt/test/orc/TestCases/Darwin/arm64/objc-imageinfo.S
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@

// Check error conditions.

// RUN: not %llvm_jitlink %t/main.o %t/objc_old.o %t/objc_new.o 2>&1 | FileCheck %s -check-prefix=CATEGORY
// CATEGORY: ObjC category class property support in {{.*}} does not match first registered flags

// RUN: not %llvm_jitlink %t/main.o %t/swift_4.o %t/swift_5.o 2>&1 | FileCheck %s -check-prefix=SWIFT_ABI
// SWIFT_ABI: Swift ABI version in {{.*}} does not match first registered flags

Expand All @@ -47,6 +44,14 @@
// RUN: %llvm_jitlink -debug-only=orc %t/main.o %t/swift_59.o %t/swift_5.o %t/objc_new.o 2>&1 | FileCheck %s -check-prefix=SWIFT_MIX3
// SWIFT_MIX3: MachOPlatform: Merging __objc_imageinfo flags for main {{.*}} -> 0x5000740

// Disable categories.
// RUN: %llvm_jitlink -debug-only=orc %t/main.o %t/objc_old.o %t/objc_new.o 2>&1 | FileCheck %s -check-prefix=SWIFT_MIX4
// SWIFT_MIX4: MachOPlatform: Merging __objc_imageinfo flags for main {{.*}} -> 0x0000

// Disable signed class_ro.
// RUN: %llvm_jitlink -debug-only=orc %t/main.o %t/objc_new.o %t/objc_new_signed_ro.o 2>&1 | FileCheck %s -check-prefix=SWIFT_MIX5
// SWIFT_MIX5: MachOPlatform: Merging __objc_imageinfo flags for main {{.*}} -> 0x0040

//--- main.S
.section __TEXT,__text,regular,pure_instructions
.globl _main
Expand Down Expand Up @@ -76,6 +81,17 @@ L_OBJC_IMAGE_INFO:
.long 0
.long 64

//--- objc_new_signed_ro.S
.section __TEXT,__text,regular,pure_instructions
.globl _objc3
_objc3:
ret

.section __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
.long 0
.long 80

//--- swift_4.S
.section __TEXT,__text,regular,pure_instructions
.globl _swift4
Expand Down
22 changes: 19 additions & 3 deletions compiler-rt/test/orc/TestCases/Darwin/x86-64/objc-imageinfo.S
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@

// Check error conditions.

// RUN: not %llvm_jitlink %t/main.o %t/objc_old.o %t/objc_new.o 2>&1 | FileCheck %s -check-prefix=CATEGORY
// CATEGORY: ObjC category class property support in {{.*}} does not match first registered flags

// RUN: not %llvm_jitlink %t/main.o %t/swift_4.o %t/swift_5.o 2>&1 | FileCheck %s -check-prefix=SWIFT_ABI
// SWIFT_ABI: Swift ABI version in {{.*}} does not match first registered flags

Expand All @@ -47,6 +44,14 @@
// RUN: %llvm_jitlink -debug-only=orc %t/main.o %t/swift_59.o %t/swift_5.o %t/objc_new.o 2>&1 | FileCheck %s -check-prefix=SWIFT_MIX3
// SWIFT_MIX3: MachOPlatform: Merging __objc_imageinfo flags for main {{.*}} -> 0x5000740

// Disable categories.
// RUN: %llvm_jitlink -debug-only=orc %t/main.o %t/objc_old.o %t/objc_new.o 2>&1 | FileCheck %s -check-prefix=SWIFT_MIX4
// SWIFT_MIX4: MachOPlatform: Merging __objc_imageinfo flags for main {{.*}} -> 0x0000

// Disable signed class_ro.
// RUN: %llvm_jitlink -debug-only=orc %t/main.o %t/objc_new.o %t/objc_new_signed_ro.o 2>&1 | FileCheck %s -check-prefix=SWIFT_MIX5
// SWIFT_MIX5: MachOPlatform: Merging __objc_imageinfo flags for main {{.*}} -> 0x0040

//--- main.S
.section __TEXT,__text,regular,pure_instructions
.globl _main
Expand Down Expand Up @@ -76,6 +81,17 @@ L_OBJC_IMAGE_INFO:
.long 0
.long 64

//--- objc_new_signed_ro.S
.section __TEXT,__text,regular,pure_instructions
.globl _objc3
_objc3:
ret

.section __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
.long 0
.long 80

//--- swift_4.S
.section __TEXT,__text,regular,pure_instructions
.globl _swift4
Expand Down
14 changes: 14 additions & 0 deletions flang/include/flang/Lower/StatementContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
#include <functional>
#include <optional>

namespace mlir {
class Location;
class Region;
} // namespace mlir

namespace fir {
class FirOpBuilder;
}

namespace Fortran::lower {

/// When lowering a statement, temporaries for intermediate results may be
Expand Down Expand Up @@ -105,6 +114,11 @@ class StatementContext {
llvm::SmallVector<std::optional<CleanupFunction>> cufs;
};

/// If \p context contains any cleanups, ensure \p region has a block, and
/// generate the cleanup inside that block.
void genCleanUpInRegionIfAny(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Region &region, StatementContext &context);

} // namespace Fortran::lower

#endif // FORTRAN_LOWER_STATEMENTCONTEXT_H
24 changes: 23 additions & 1 deletion flang/include/flang/Optimizer/HLFIR/HLFIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,8 @@ def hlfir_RegionAssignOp : hlfir_Op<"region_assign", [hlfir_OrderedAssignmentTre
}

def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp",
"ElementalAddrOp", "ForallOp", "ForallMaskOp", "WhereOp", "ElseWhereOp"]>,
"ElementalAddrOp", "ForallOp", "ForallMaskOp", "WhereOp", "ElseWhereOp",
"ExactlyOnceOp"]>,
SingleBlockImplicitTerminator<"fir::FirEndOp">, RecursivelySpeculatable,
RecursiveMemoryEffects]> {

Expand Down Expand Up @@ -1595,6 +1596,27 @@ def hlfir_ForallMaskOp : hlfir_AssignmentMaskOp<"forall_mask"> {
let hasVerifier = 1;
}

def hlfir_ExactlyOnceOp : hlfir_Op<"exactly_once", [RecursiveMemoryEffects]> {
let summary = "Execute exactly once its region in a WhereOp";
let description = [{
Inside a Where assignment, Fortran requires a non elemental call and its
arguments to be executed exactly once, regardless of the mask values.
This operation allows holding these evaluations that cannot be hoisted
until potential parent Forall loops have been created.
It also allows inlining the calls without losing the information that
these calls must be hoisted.
}];

let regions = (region SizedRegion<1>:$body);

let results = (outs AnyFortranEntity:$result);

let assemblyFormat = [{
attr-dict `:` type($result)
$body
}];
}

def hlfir_WhereOp : hlfir_AssignmentMaskOp<"where"> {
let summary = "Represent a Fortran where construct or statement";
let description = [{
Expand Down
45 changes: 23 additions & 22 deletions flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3687,22 +3687,6 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return hlfir::Entity{valueAndPair.first};
}

static void
genCleanUpInRegionIfAny(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Region &region,
Fortran::lower::StatementContext &context) {
if (!context.hasCode())
return;
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
if (region.empty())
builder.createBlock(&region);
else
builder.setInsertionPointToEnd(&region.front());
context.finalizeAndPop();
hlfir::YieldOp::ensureTerminator(region, builder, loc);
builder.restoreInsertionPoint(insertPt);
}

bool firstDummyIsPointerOrAllocatable(
const Fortran::evaluate::ProcedureRef &userDefinedAssignment) {
using DummyAttr = Fortran::evaluate::characteristics::DummyDataObject::Attr;
Expand Down Expand Up @@ -3928,23 +3912,24 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::lower::StatementContext rhsContext;
hlfir::Entity rhs = evaluateRhs(rhsContext);
auto rhsYieldOp = builder.create<hlfir::YieldOp>(loc, rhs);
genCleanUpInRegionIfAny(loc, builder, rhsYieldOp.getCleanup(), rhsContext);
Fortran::lower::genCleanUpInRegionIfAny(
loc, builder, rhsYieldOp.getCleanup(), rhsContext);
// Lower LHS in its own region.
builder.createBlock(&regionAssignOp.getLhsRegion());
Fortran::lower::StatementContext lhsContext;
mlir::Value lhsYield = nullptr;
if (!lhsHasVectorSubscripts) {
hlfir::Entity lhs = evaluateLhs(lhsContext);
auto lhsYieldOp = builder.create<hlfir::YieldOp>(loc, lhs);
genCleanUpInRegionIfAny(loc, builder, lhsYieldOp.getCleanup(),
lhsContext);
Fortran::lower::genCleanUpInRegionIfAny(
loc, builder, lhsYieldOp.getCleanup(), lhsContext);
lhsYield = lhs;
} else {
hlfir::ElementalAddrOp elementalAddr =
Fortran::lower::convertVectorSubscriptedExprToElementalAddr(
loc, *this, assign.lhs, localSymbols, lhsContext);
genCleanUpInRegionIfAny(loc, builder, elementalAddr.getCleanup(),
lhsContext);
Fortran::lower::genCleanUpInRegionIfAny(
loc, builder, elementalAddr.getCleanup(), lhsContext);
lhsYield = elementalAddr.getYieldOp().getEntity();
}
assert(lhsYield && "must have been set");
Expand Down Expand Up @@ -4299,7 +4284,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
loc, *this, *maskExpr, localSymbols, maskContext);
mask = hlfir::loadTrivialScalar(loc, *builder, mask);
auto yieldOp = builder->create<hlfir::YieldOp>(loc, mask);
genCleanUpInRegionIfAny(loc, *builder, yieldOp.getCleanup(), maskContext);
Fortran::lower::genCleanUpInRegionIfAny(loc, *builder, yieldOp.getCleanup(),
maskContext);
}
void genFIR(const Fortran::parser::WhereConstructStmt &stmt) {
const Fortran::semantics::SomeExpr *maskExpr = Fortran::semantics::GetExpr(
Expand Down Expand Up @@ -5599,3 +5585,18 @@ Fortran::lower::LoweringBridge::LoweringBridge(
fir::support::setMLIRDataLayout(*module.get(),
targetMachine.createDataLayout());
}

void Fortran::lower::genCleanUpInRegionIfAny(
mlir::Location loc, fir::FirOpBuilder &builder, mlir::Region &region,
Fortran::lower::StatementContext &context) {
if (!context.hasCode())
return;
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
if (region.empty())
builder.createBlock(&region);
else
builder.setInsertionPointToEnd(&region.front());
context.finalizeAndPop();
hlfir::YieldOp::ensureTerminator(region, builder, loc);
builder.restoreInsertionPoint(insertPt);
}
38 changes: 38 additions & 0 deletions flang/lib/Lower/ConvertCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2682,10 +2682,48 @@ bool Fortran::lower::isIntrinsicModuleProcRef(
return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC);
}

static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) {
// The MASK of the outer WHERE is not masked itself.
mlir::Operation *op = builder.getRegion().getParentOp();
return op && op->getParentOfType<hlfir::WhereOp>();
}

std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType,
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
auto &builder = converter.getFirOpBuilder();
if (resultType && !procRef.IsElemental() &&
isInWhereMaskedExpression(builder) &&
!builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) {
// Non elemental calls inside a where-assignment-stmt must be executed
// exactly once without mask control. Lower them in a special region so that
// this can be enforced whenscheduling forall/where expression evaluations.
Fortran::lower::StatementContext localStmtCtx;
mlir::Type bogusType = builder.getIndexType();
auto exactlyOnce = builder.create<hlfir::ExactlyOnceOp>(loc, bogusType);
mlir::Block *block = builder.createBlock(&exactlyOnce.getBody());
builder.setInsertionPointToStart(block);
CallContext callContext(procRef, resultType, loc, converter, symMap,
localStmtCtx);
std::optional<hlfir::EntityWithAttributes> res =
genProcedureRef(callContext);
assert(res.has_value() && "must be a function");
auto yield = builder.create<hlfir::YieldOp>(loc, *res);
Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(),
localStmtCtx);
builder.setInsertionPointAfter(exactlyOnce);
exactlyOnce->getResult(0).setType(res->getType());
if (hlfir::isFortranValue(exactlyOnce.getResult()))
return hlfir::EntityWithAttributes{exactlyOnce.getResult()};
// Create hlfir.declare for the result to satisfy
// hlfir::EntityWithAttributes requirements.
auto [exv, cleanup] = hlfir::translateToExtendedValue(
loc, builder, hlfir::Entity{exactlyOnce});
assert(!cleanup && "resut is a variable");
return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result",
fir::FortranVariableFlagsAttr{});
}
CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx);
return genProcedureRef(callContext);
}
Expand Down
68 changes: 27 additions & 41 deletions flang/lib/Lower/OpenMP/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1959,6 +1959,9 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter,
const List<Clause> &clauses = item->clauses;

switch (llvm::omp::Directive dir = item->id) {
case llvm::omp::Directive::OMPD_barrier:
genBarrierOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_distribute:
genDistributeOp(converter, symTable, semaCtx, eval, loc, clauses, queue,
item);
Expand All @@ -1968,22 +1971,25 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter,
break;
case llvm::omp::Directive::OMPD_loop:
case llvm::omp::Directive::OMPD_masked:
case llvm::omp::Directive::OMPD_tile:
case llvm::omp::Directive::OMPD_unroll:
TODO(loc, "Unhandled loop directive (" +
llvm::omp::getOpenMPDirectiveName(dir) + ")");
break;
case llvm::omp::Directive::OMPD_master:
genMasterOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item);
break;
case llvm::omp::Directive::OMPD_ordered:
// Block-associated "ordered" construct.
genOrderedRegionOp(converter, symTable, semaCtx, eval, loc, clauses, queue,
item);
break;
case llvm::omp::Directive::OMPD_parallel:
genParallelOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item,
/*outerCombined=*/false);
break;
case llvm::omp::Directive::OMPD_section:
genSectionOp(converter, symTable, semaCtx, eval, loc, /*clauses=*/{}, queue,
item);
break;
case llvm::omp::Directive::OMPD_sections:
genSectionsOp(converter, symTable, semaCtx, eval, loc, clauses, queue,
item);
Expand Down Expand Up @@ -2025,16 +2031,28 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter,
genTaskloopOp(converter, symTable, semaCtx, eval, loc, clauses, queue,
item);
break;
case llvm::omp::Directive::OMPD_taskwait:
genTaskwaitOp(converter, symTable, semaCtx, eval, loc, clauses, queue,
item);
break;
case llvm::omp::Directive::OMPD_taskyield:
genTaskyieldOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_teams:
genTeamsOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item);
break;
case llvm::omp::Directive::OMPD_tile:
case llvm::omp::Directive::OMPD_unroll:
TODO(loc, "Unhandled loop directive (" +
llvm::omp::getOpenMPDirectiveName(dir) + ")");
// case llvm::omp::Directive::OMPD_workdistribute:
case llvm::omp::Directive::OMPD_workshare:
// FIXME: Workshare is not a commonly used OpenMP construct, an
// implementation for this feature will come later. For the codes
// that use this construct, add a single construct for now.
genSingleOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item);
break;

// Composite constructs
case llvm::omp::Directive::OMPD_distribute_parallel_do:
genCompositeDistributeParallelDo(converter, symTable, semaCtx, eval, loc,
Expand Down Expand Up @@ -2174,45 +2192,14 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
ConstructQueue queue{
buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,
eval, directive.source, directive.v, clauses)};

switch (directive.v) {
default:
break;
case llvm::omp::Directive::OMPD_barrier:
genBarrierOp(converter, symTable, semaCtx, eval, currentLocation, queue,
queue.begin());
break;
case llvm::omp::Directive::OMPD_taskwait:
genTaskwaitOp(converter, symTable, semaCtx, eval, currentLocation, clauses,
queue, queue.begin());
break;
case llvm::omp::Directive::OMPD_taskyield:
genTaskyieldOp(converter, symTable, semaCtx, eval, currentLocation, queue,
queue.begin());
break;
case llvm::omp::Directive::OMPD_target_data:
genTargetDataOp(converter, symTable, semaCtx, eval, currentLocation,
clauses, queue, queue.begin());
break;
case llvm::omp::Directive::OMPD_target_enter_data:
genTargetEnterExitUpdateDataOp<mlir::omp::TargetEnterDataOp>(
converter, symTable, semaCtx, currentLocation, clauses, queue,
queue.begin());
break;
case llvm::omp::Directive::OMPD_target_exit_data:
genTargetEnterExitUpdateDataOp<mlir::omp::TargetExitDataOp>(
converter, symTable, semaCtx, currentLocation, clauses, queue,
queue.begin());
break;
case llvm::omp::Directive::OMPD_target_update:
genTargetEnterExitUpdateDataOp<mlir::omp::TargetUpdateOp>(
converter, symTable, semaCtx, currentLocation, clauses, queue,
queue.begin());
break;
case llvm::omp::Directive::OMPD_ordered:
if (directive.v == llvm::omp::Directive::OMPD_ordered) {
// Standalone "ordered" directive.
genOrderedOp(converter, symTable, semaCtx, eval, currentLocation, clauses,
queue, queue.begin());
break;
} else {
// Dispatch handles the "block-associated" variant of "ordered".
genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,
queue.begin());
}
}

Expand Down Expand Up @@ -2466,8 +2453,7 @@ genOMP(Fortran::lower::AbstractConverter &converter,
ConstructQueue queue{buildConstructQueue(
converter.getFirOpBuilder().getModule(), semaCtx, eval,
sectionConstruct.source, llvm::omp::Directive::OMPD_section, {})};
genSectionOp(converter, symTable, semaCtx, eval, loc,
/*clauses=*/{}, queue, queue.begin());
genOMPDispatch(converter, symTable, semaCtx, eval, loc, queue, queue.begin());
}

static void
Expand Down
130 changes: 108 additions & 22 deletions flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ namespace {
/// expression and allows splitting the generation of the none elemental part
/// from the elemental part.
struct MaskedArrayExpr {
MaskedArrayExpr(mlir::Location loc, mlir::Region &region);
MaskedArrayExpr(mlir::Location loc, mlir::Region &region,
bool isOuterMaskExpr);

/// Generate the none elemental part. Must be called outside of the
/// loops created for the WHERE construct.
Expand All @@ -79,16 +80,25 @@ struct MaskedArrayExpr {
void generateNoneElementalCleanupIfAny(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper);

/// Helper to clone the clean-ups of the masked expr region terminator.
/// This is called outside of the loops for the initial mask, and inside
/// the loops for the other masked expressions.
mlir::Operation *generateMaskedExprCleanUps(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper);

mlir::Location loc;
mlir::Region &region;
/// Was generateNoneElementalPart called?
bool noneElementalPartWasGenerated = false;
/// Set of operations that form the elemental parts of the
/// expression evaluation. These are the hlfir.elemental and
/// hlfir.elemental_addr that form the elemental tree producing
/// the expression value. hlfir.elemental that produce values
/// used inside transformational operations are not part of this set.
llvm::SmallSet<mlir::Operation *, 4> elementalParts{};
/// Was generateNoneElementalPart called?
bool noneElementalPartWasGenerated = false;
/// Is this expression the mask expression of the outer where statement?
/// It is special because its evaluation is not masked by anything yet.
bool isOuterMaskExpr = false;
};
} // namespace

Expand Down Expand Up @@ -202,7 +212,7 @@ class OrderedAssignmentRewriter {
/// This method returns the scalar element (that may have been previously
/// saved) for the current indices inside the where loop.
mlir::Value generateMaskedEntity(mlir::Location loc, mlir::Region &region) {
MaskedArrayExpr maskedExpr(loc, region);
MaskedArrayExpr maskedExpr(loc, region, /*isOuterMaskExpr=*/!whereLoopNest);
return generateMaskedEntity(maskedExpr);
}
mlir::Value generateMaskedEntity(MaskedArrayExpr &maskedExpr);
Expand Down Expand Up @@ -524,7 +534,8 @@ void OrderedAssignmentRewriter::pre(hlfir::WhereOp whereOp) {
return;
}
// The mask was not evaluated yet or can be safely re-evaluated.
MaskedArrayExpr mask(loc, whereOp.getMaskRegion());
MaskedArrayExpr mask(loc, whereOp.getMaskRegion(),
/*isOuterMaskExpr=*/true);
mask.generateNoneElementalPart(builder, mapper);
mlir::Value shape = mask.generateShape(builder, mapper);
whereLoopNest = hlfir::genLoopNest(loc, builder, shape);
Expand Down Expand Up @@ -628,6 +639,13 @@ OrderedAssignmentRewriter::getIfSaved(mlir::Region &region) {
return std::nullopt;
}

static hlfir::YieldOp getYield(mlir::Region &region) {
auto yield = mlir::dyn_cast_or_null<hlfir::YieldOp>(
region.back().getOperations().back());
assert(yield && "region computing entities must end with a YieldOp");
return yield;
}

OrderedAssignmentRewriter::ValueAndCleanUp
OrderedAssignmentRewriter::generateYieldedEntity(
mlir::Region &region, std::optional<mlir::Type> castToType) {
Expand All @@ -644,9 +662,7 @@ OrderedAssignmentRewriter::generateYieldedEntity(
}

assert(region.hasOneBlock() && "region must contain one block");
auto oldYield = mlir::dyn_cast_or_null<hlfir::YieldOp>(
region.back().getOperations().back());
assert(oldYield && "region computing entities must end with a YieldOp");
auto oldYield = getYield(region);
mlir::Block::OpListType &ops = region.back().getOperations();

// Inside Forall, scalars that do not depend on forall indices can be hoisted
Expand Down Expand Up @@ -792,8 +808,15 @@ OrderedAssignmentRewriter::generateMaskedEntity(MaskedArrayExpr &maskedExpr) {
// at the current insertion point (inside the where loops, and any fir.if
// generated for previous masks).
builder.restoreInsertionPoint(insertionPoint);
return maskedExpr.generateElementalParts(
mlir::Value scalar = maskedExpr.generateElementalParts(
builder, whereLoopNest->oneBasedIndices, mapper);
/// Generate cleanups for the elemental parts inside the loops (setting the
/// location so that the assignment will be generated before the cleanups).
if (!maskedExpr.isOuterMaskExpr)
if (mlir::Operation *firstCleanup =
maskedExpr.generateMaskedExprCleanUps(builder, mapper))
builder.setInsertionPoint(firstCleanup);
return scalar;
}

void OrderedAssignmentRewriter::generateCleanupIfAny(
Expand Down Expand Up @@ -887,8 +910,9 @@ gatherElementalTree(hlfir::ElementalOpInterface elemental,
}
}

MaskedArrayExpr::MaskedArrayExpr(mlir::Location loc, mlir::Region &region)
: loc{loc}, region{region} {
MaskedArrayExpr::MaskedArrayExpr(mlir::Location loc, mlir::Region &region,
bool isOuterMaskExpr)
: loc{loc}, region{region}, isOuterMaskExpr{isOuterMaskExpr} {
mlir::Operation &terminator = region.back().back();
if (auto elementalAddr =
mlir::dyn_cast<hlfir::ElementalOpInterface>(terminator)) {
Expand All @@ -907,13 +931,36 @@ void MaskedArrayExpr::generateNoneElementalPart(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper) {
assert(!noneElementalPartWasGenerated &&
"none elemental parts already generated");
// Clone all operations, except the elemental and the final yield.
mlir::Block::OpListType &ops = region.back().getOperations();
assert(!ops.empty() && "yield block cannot be empty");
auto end = ops.end();
for (auto opIt = ops.begin(); std::next(opIt) != end; ++opIt)
if (!elementalParts.contains(&*opIt))
(void)builder.clone(*opIt, mapper);
if (isOuterMaskExpr) {
// The outer mask expression is actually not masked, it is dealt as
// such so that its elemental part, if any, can be inlined in the WHERE
// loops. But all of the operations outside of hlfir.elemental/
// hlfir.elemental_addr must be emitted now because their value may be
// required to deduce the mask shape and the WHERE loop bounds.
for (mlir::Operation &op : region.back().without_terminator())
if (!elementalParts.contains(&op))
(void)builder.clone(op, mapper);
} else {
// For actual masked expressions, Fortran requires elemental expressions,
// even the scalar ones that are not encoded with hlfir.elemental, to be
// evaluated only when the mask is true. Blindly hoisting all scalar SSA
// tree could be wrong if the scalar computation has side effects and
// would never have been evaluated (e.g. division by zero) if the mask
// is fully false. See F'2023 10.2.3.2 point 10.
// Clone only the bodies of all hlfir.exactly_once operations, which contain
// the evaluation of sub-expression tree whose root was a non elemental
// function call at the Fortran level (the call itself may have been inlined
// since). These must be evaluated only once as per F'2023 10.2.3.2 point 9.
for (mlir::Operation &op : region.back().without_terminator())
if (auto exactlyOnce = mlir::dyn_cast<hlfir::ExactlyOnceOp>(op)) {
for (mlir::Operation &subOp :
exactlyOnce.getBody().back().without_terminator())
(void)builder.clone(subOp, mapper);
mlir::Value oldYield = getYield(exactlyOnce.getBody()).getEntity();
auto newYield = mapper.lookupOrDefault(oldYield);
mapper.map(exactlyOnce.getResult(), newYield);
}
}
noneElementalPartWasGenerated = true;
}

Expand Down Expand Up @@ -942,6 +989,15 @@ MaskedArrayExpr::generateElementalParts(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper) {
assert(noneElementalPartWasGenerated &&
"non elemental part must have been generated");
if (!isOuterMaskExpr) {
// Clone all operations that are not hlfir.exactly_once and that are not
// hlfir.elemental/hlfir.elemental_addr.
for (mlir::Operation &op : region.back().without_terminator())
if (!mlir::isa<hlfir::ExactlyOnceOp>(op) && !elementalParts.contains(&op))
(void)builder.clone(op, mapper);
// For the outer mask, this was already done outside of the loop.
}
// Clone and "index" bodies of hlfir.elemental/hlfir.elemental_addr.
mlir::Operation &terminator = region.back().back();
hlfir::ElementalOpInterface elemental =
mlir::dyn_cast<hlfir::ElementalAddrOp>(terminator);
Expand All @@ -966,8 +1022,11 @@ MaskedArrayExpr::generateElementalParts(fir::FirOpBuilder &builder,
mustRecursivelyInline);
}

void MaskedArrayExpr::generateNoneElementalCleanupIfAny(
fir::FirOpBuilder &builder, mlir::IRMapping &mapper) {
mlir::Operation *
MaskedArrayExpr::generateMaskedExprCleanUps(fir::FirOpBuilder &builder,
mlir::IRMapping &mapper) {
// Clone the clean-ups from the region itself, except for the destroy
// of the hlfir.elemental that have been inlined.
mlir::Operation &terminator = region.back().back();
mlir::Region *cleanupRegion = nullptr;
if (auto elementalAddr = mlir::dyn_cast<hlfir::ElementalAddrOp>(terminator)) {
Expand All @@ -977,12 +1036,39 @@ void MaskedArrayExpr::generateNoneElementalCleanupIfAny(
cleanupRegion = &yieldOp.getCleanup();
}
if (cleanupRegion->empty())
return;
return nullptr;
mlir::Operation *firstNewCleanup = nullptr;
for (mlir::Operation &op : cleanupRegion->front().without_terminator()) {
if (auto destroy = mlir::dyn_cast<hlfir::DestroyOp>(op))
if (elementalParts.contains(destroy.getExpr().getDefiningOp()))
continue;
(void)builder.clone(op, mapper);
mlir::Operation *cleanup = builder.clone(op, mapper);
if (!firstNewCleanup)
firstNewCleanup = cleanup;
}
return firstNewCleanup;
}

void MaskedArrayExpr::generateNoneElementalCleanupIfAny(
fir::FirOpBuilder &builder, mlir::IRMapping &mapper) {
if (!isOuterMaskExpr) {
// Clone clean-ups of hlfir.exactly_once operations (in reverse order
// to properly deal with stack restores).
for (mlir::Operation &op :
llvm::reverse(region.back().without_terminator()))
if (auto exactlyOnce = mlir::dyn_cast<hlfir::ExactlyOnceOp>(op)) {
mlir::Region &cleanupRegion =
getYield(exactlyOnce.getBody()).getCleanup();
if (!cleanupRegion.empty())
for (mlir::Operation &cleanupOp :
cleanupRegion.front().without_terminator())
(void)builder.clone(cleanupOp, mapper);
}
} else {
// For the outer mask, the region clean-ups must be generated
// outside of the loops since the mask non hlfir.elemental part
// is generated before the loops.
generateMaskedExprCleanUps(builder, mapper);
}
}

Expand Down
4 changes: 4 additions & 0 deletions flang/test/Driver/print-resource-dir.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
! DEFINE: %{resource_dir} = %S/Inputs/resource_dir
! RUN: %flang -print-resource-dir -resource-dir=%{resource_dir}.. \
! RUN: | FileCheck -check-prefix=PRINT-RESOURCE-DIR -DFILE=%{resource_dir} %s
! PRINT-RESOURCE-DIR: [[FILE]]
9 changes: 6 additions & 3 deletions flang/test/HLFIR/order_assignments/impure-where.fir
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ func.func @test_elsewhere_impure_mask(%x: !fir.ref<!fir.array<10xi32>>, %y: !fir
hlfir.yield %mask : !fir.ref<!fir.array<10x!fir.logical<4>>>
} do {
hlfir.elsewhere mask {
%mask2 = fir.call @impure() : () -> !fir.heap<!fir.array<10x!fir.logical<4>>>
hlfir.yield %mask2 : !fir.heap<!fir.array<10x!fir.logical<4>>> cleanup {
fir.freemem %mask2 : !fir.heap<!fir.array<10x!fir.logical<4>>>
%mask2 = hlfir.exactly_once : !fir.heap<!fir.array<10x!fir.logical<4>>> {
%imp = fir.call @impure() : () -> !fir.heap<!fir.array<10x!fir.logical<4>>>
hlfir.yield %imp : !fir.heap<!fir.array<10x!fir.logical<4>>> cleanup {
fir.freemem %imp : !fir.heap<!fir.array<10x!fir.logical<4>>>
}
}
hlfir.yield %mask2 : !fir.heap<!fir.array<10x!fir.logical<4>>>
} do {
hlfir.region_assign {
hlfir.yield %y : !fir.ref<!fir.array<10xi32>>
Expand Down
2 changes: 1 addition & 1 deletion flang/test/HLFIR/order_assignments/inlined-stack-temp.fir
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ func.func @test_where_rhs_save(%x: !fir.ref<!fir.array<10xi32>>, %mask: !fir.ref
// CHECK: %[[VAL_7:.*]] = arith.constant 10 : index
// CHECK: %[[VAL_8:.*]] = fir.shape %[[VAL_7]] : (index) -> !fir.shape<1>
// CHECK: %[[VAL_9:.*]] = arith.constant 1 : index
// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_5]]:%[[VAL_4]]:%[[VAL_3]]) shape %[[VAL_6]] : (!fir.ref<!fir.array<10xi32>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
// CHECK: %[[VAL_11:.*]] = arith.constant 0 : index
// CHECK: %[[VAL_12:.*]] = arith.subi %[[VAL_7]], %[[VAL_9]] : index
// CHECK: %[[VAL_13:.*]] = arith.addi %[[VAL_12]], %[[VAL_9]] : index
Expand All @@ -300,6 +299,7 @@ func.func @test_where_rhs_save(%x: !fir.ref<!fir.array<10xi32>>, %mask: !fir.ref
// CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_23]] : !fir.ref<!fir.logical<4>>
// CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_24]] : (!fir.logical<4>) -> i1
// CHECK: fir.if %[[VAL_25]] {
// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_5]]:%[[VAL_4]]:%[[VAL_3]]) shape %[[VAL_6]] : (!fir.ref<!fir.array<10xi32>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
// CHECK: %[[VAL_26:.*]] = hlfir.designate %[[VAL_10]] (%[[VAL_22]]) : (!fir.ref<!fir.array<10xi32>>, index) -> !fir.ref<i32>
// CHECK: %[[VAL_27:.*]] = fir.load %[[VAL_26]] : !fir.ref<i32>
// CHECK: %[[VAL_28:.*]] = fir.load %[[VAL_2]] : !fir.ref<index>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func.func @_QPtest1() {
%7 = fir.call @_FortranADestroy(%6) fastmath<contract> : (!fir.box<none>) -> none
}
} to {
hlfir.yield %2#0 : !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
hlfir.yield %2#0 : !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
} user_defined_assign (%arg0: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) to (%arg1: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) {
%3 = fir.embox %arg1 : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
%4 = fir.convert %3 : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.class<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
Expand Down Expand Up @@ -119,7 +119,7 @@ func.func @_QPtest2() {
fir.call @llvm.stackrestore.p0(%4) fastmath<contract> : (!fir.ref<i8>) -> ()
}
} to {
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
} user_defined_assign (%arg0: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) to (%arg1: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) {
%4 = fir.embox %arg1 : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
%5 = fir.convert %4 : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.class<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
Expand Down Expand Up @@ -193,18 +193,22 @@ func.func @_QPtest3(%arg0: !fir.ref<!fir.array<2xi32>> {fir.bindc_name = "y"}) {
}
} do {
hlfir.region_assign {
%5 = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
%6 = fir.call @_QPnew_obja() fastmath<contract> : () -> !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
fir.save_result %6 to %0(%2) : !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>
%7:2 = hlfir.declare %0(%2) {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>)
hlfir.yield %7#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>> cleanup {
%8 = fir.embox %0(%2) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
%9 = fir.convert %8 : (!fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>) -> !fir.box<none>
%10 = fir.call @_FortranADestroy(%9) fastmath<contract> : (!fir.box<none>) -> none
fir.call @llvm.stackrestore.p0(%5) fastmath<contract> : (!fir.ref<i8>) -> ()
%5 = hlfir.exactly_once : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>> {
%7 = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
%8 = fir.call @_QPnew_obja() fastmath<contract> : () -> !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
fir.save_result %8 to %0(%2) : !fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>
%9:2 = hlfir.declare %0(%2) {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>)
hlfir.yield %9#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>> cleanup {
%10 = fir.embox %0(%2) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
%11 = fir.convert %10 : (!fir.box<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>) -> !fir.box<none>
%12 = fir.call @_FortranADestroy(%11) fastmath<contract> : (!fir.box<none>) -> none
fir.call @llvm.stackrestore.p0(%7) fastmath<contract> : (!fir.ref<i8>) -> ()
}
}
%6:2 = hlfir.declare %5(%2) {uniq_name = ".func.pointer.result"} : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>)
hlfir.yield %6#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
} to {
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
hlfir.yield %3#0 : !fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>
} user_defined_assign (%arg1: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) to (%arg2: !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) {
%5 = fir.embox %arg2 : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
%6 = fir.convert %5 : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.class<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
Expand Down Expand Up @@ -246,7 +250,8 @@ func.func @_QPtest3(%arg0: !fir.ref<!fir.array<2xi32>> {fir.bindc_name = "y"}) {
// CHECK: %[[VAL_30:.*]] = fir.load %[[VAL_29]] : !fir.ref<!fir.logical<4>>
// CHECK: %[[VAL_31:.*]] = fir.convert %[[VAL_30]] : (!fir.logical<4>) -> i1
// CHECK: fir.if %[[VAL_31]] {
// CHECK: %[[VAL_32:.*]] = hlfir.designate %[[VAL_20]]#0 (%[[VAL_28]]) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, index) -> !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
// CHECK: %[[VAL_20B:.*]]:2 = hlfir.declare %[[VAL_20]]#0(%[[VAL_7]]) {uniq_name = ".func.pointer.result"}
// CHECK: %[[VAL_32:.*]] = hlfir.designate %[[VAL_20B]]#0 (%[[VAL_28]]) : (!fir.ref<!fir.array<2x!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>>, index) -> !fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
// CHECK: %[[VAL_33:.*]] = fir.embox %[[VAL_32]] : (!fir.ref<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>
// CHECK: %[[VAL_34:.*]] = fir.convert %[[VAL_33]] : (!fir.box<!fir.type<_QMtypesTud_assign{x:!fir.box<!fir.ptr<i32>>}>>) -> !fir.box<none>
// CHECK: %[[VAL_35:.*]] = fir.call @_FortranAPushValue(%[[VAL_27]], %[[VAL_34]]) : (!fir.llvm_ptr<i8>, !fir.box<none>) -> none
Expand Down
44 changes: 44 additions & 0 deletions flang/test/HLFIR/order_assignments/where-cleanup.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Test hlfir.where masked region cleanup lowering (the freemem in the tests).
// RUN: fir-opt %s --lower-hlfir-ordered-assignments | FileCheck %s

func.func @loop_cleanup(%mask : !fir.ref<!fir.array<2x!fir.logical<4>>>, %x : !fir.ref<!fir.array<2xf32>>, %y : !fir.ref<!fir.array<2xf32>>) {
hlfir.where {
%1 = fir.allocmem !fir.array<10xi32>
hlfir.yield %mask : !fir.ref<!fir.array<2x!fir.logical<4>>> cleanup {
fir.freemem %1 : !fir.heap<!fir.array<10xi32>>
}
} do {
hlfir.region_assign {
%1 = fir.allocmem !fir.array<1xi32>
%2 = fir.allocmem !fir.array<2xi32>
hlfir.yield %x : !fir.ref<!fir.array<2xf32>> cleanup {
fir.freemem %2 : !fir.heap<!fir.array<2xi32>>
fir.freemem %1 : !fir.heap<!fir.array<1xi32>>
}
} to {
%1 = fir.allocmem !fir.array<3xi32>
%2 = fir.allocmem !fir.array<4xi32>
hlfir.yield %y : !fir.ref<!fir.array<2xf32>> cleanup {
fir.freemem %2 : !fir.heap<!fir.array<4xi32>>
fir.freemem %1 : !fir.heap<!fir.array<3xi32>>
}
}
}
return
}
// CHECK-LABEL: func.func @loop_cleanup(
// CHECK: %[[VAL_3:.*]] = fir.allocmem !fir.array<10xi32>
// CHECK: fir.do_loop
// CHECK: fir.if
// CHECK: %[[VAL_11:.*]] = fir.allocmem !fir.array<1xi32>
// CHECK: %[[VAL_12:.*]] = fir.allocmem !fir.array<2xi32>
// CHECK: %[[VAL_14:.*]] = fir.allocmem !fir.array<3xi32>
// CHECK: %[[VAL_15:.*]] = fir.allocmem !fir.array<4xi32>
// CHECK: hlfir.assign
// CHECK: fir.freemem %[[VAL_15]] : !fir.heap<!fir.array<4xi32>>
// CHECK: fir.freemem %[[VAL_14]] : !fir.heap<!fir.array<3xi32>>
// CHECK: fir.freemem %[[VAL_12]] : !fir.heap<!fir.array<2xi32>>
// CHECK: fir.freemem %[[VAL_11]] : !fir.heap<!fir.array<1xi32>>
// CHECK: }
// CHECK: }
// CHECK: fir.freemem %[[VAL_3]] : !fir.heap<!fir.array<10xi32>>
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,6 @@ func.func @inside_forall(%arg0: !fir.ref<!fir.array<10x20xf32>>, %arg1: !fir.ref
// CHECK: fir.do_loop %[[VAL_15:.*]] = %[[VAL_12]] to %[[VAL_13]] step %[[VAL_14]] {
// CHECK: %[[VAL_16:.*]] = fir.convert %[[VAL_15]] : (index) -> i32
// CHECK: %[[VAL_17:.*]] = arith.constant 1 : index
// CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_16]] : (i32) -> i64
// CHECK: %[[VAL_19:.*]] = hlfir.designate %[[VAL_9]]#0 (%[[VAL_18]], %[[VAL_2]]:%[[VAL_7]]:%[[VAL_2]]) shape %[[VAL_10]] : (!fir.ref<!fir.array<10x20xf32>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<20xf32>>
// CHECK: fir.do_loop %[[VAL_20:.*]] = %[[VAL_17]] to %[[VAL_7]] step %[[VAL_17]] {
// CHECK: %[[VAL_21:.*]] = hlfir.designate %[[VAL_11]]#0 (%[[VAL_20]]) : (!fir.ref<!fir.array<20xf32>>, index) -> !fir.ref<f32>
// CHECK: %[[VAL_22:.*]] = fir.load %[[VAL_21]] : !fir.ref<f32>
Expand All @@ -300,6 +298,8 @@ func.func @inside_forall(%arg0: !fir.ref<!fir.array<10x20xf32>>, %arg1: !fir.ref
// CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_24]] : (!fir.logical<4>) -> i1
// CHECK: fir.if %[[VAL_25]] {
// CHECK: %[[VAL_26:.*]] = hlfir.designate %[[VAL_11]]#0 (%[[VAL_20]]) : (!fir.ref<!fir.array<20xf32>>, index) -> !fir.ref<f32>
// CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_16]] : (i32) -> i64
// CHECK: %[[VAL_19:.*]] = hlfir.designate %[[VAL_9]]#0 (%[[VAL_18]], %[[VAL_2]]:%[[VAL_7]]:%[[VAL_2]]) shape %[[VAL_10]] : (!fir.ref<!fir.array<10x20xf32>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<20xf32>>
// CHECK: %[[VAL_27:.*]] = hlfir.designate %[[VAL_19]] (%[[VAL_20]]) : (!fir.box<!fir.array<20xf32>>, index) -> !fir.ref<f32>
// CHECK: hlfir.assign %[[VAL_26]] to %[[VAL_27]] : !fir.ref<f32>, !fir.ref<f32>
// CHECK: }
Expand Down
50 changes: 50 additions & 0 deletions flang/test/HLFIR/order_assignments/where-hoisting.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
! Test that scalar expressions are not hoisted from WHERE loops
! when they do not appear
! RUN: bbc -hlfir -o - -pass-pipeline="builtin.module(lower-hlfir-ordered-assignments)" %s | FileCheck %s

subroutine do_not_hoist_div(n, mask, a)
integer :: a(10), n
logical :: mask(10)
where(mask) a=1/n
end subroutine
! CHECK-LABEL: func.func @_QPdo_not_hoist_div(
! CHECK-NOT: arith.divsi
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.if {{.*}} {
! CHECK: arith.divsi
! CHECK: }
! CHECK: }

subroutine do_not_hoist_optional(n, mask, a)
integer :: a(10)
integer, optional :: n
logical :: mask(10)
where(mask) a=n
end subroutine
! CHECK-LABEL: func.func @_QPdo_not_hoist_optional(
! CHECK: %[[VAL_9:.*]]:2 = hlfir.declare {{.*}}"_QFdo_not_hoist_optionalEn"
! CHECK-NOT: fir.load %[[VAL_9]]
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.if {{.*}} {
! CHECK: %[[VAL_15:.*]] = fir.load %[[VAL_9]]#0 : !fir.ref<i32>
! CHECK: }
! CHECK: }

subroutine hoist_function(n, mask, a)
integer :: a(10, 10)
integer, optional :: n
logical :: mask(10, 10)
forall (i=1:10)
where(mask(i, :)) a(i,:)=ihoist_me(i)
end forall
end subroutine
! CHECK-LABEL: func.func @_QPhoist_function(
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.call @_QPihoist_me
! CHECK: fir.do_loop {{.*}} {
! CHECK: fir.if %{{.*}} {
! CHECK-NOT: fir.call @_QPihoist_me
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK-NOT: fir.call @_QPihoist_me
198 changes: 198 additions & 0 deletions flang/test/Lower/HLFIR/where-nonelemental.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
! Test lowering of non elemental calls and there inputs inside WHERE
! constructs. These must be lowered inside hlfir.exactly_once so that
! they are properly hoisted once the loops are materialized and
! expression evaluations are scheduled.
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s

subroutine test_where(a, b, c)
real, dimension(:) :: a, b, c
interface
function logical_func1()
logical :: logical_func1(100)
end function
function logical_func2()
logical :: logical_func2(100)
end function
real elemental function elem_func(x)
real, intent(in) :: x
end function
end interface
where (logical_func1())
a = b + real_func(a+b+real_func2()) + elem_func(a)
elsewhere(logical_func2())
a(1:ifoo()) = c
end where
end subroutine
! CHECK-LABEL: func.func @_QPtest_where(
! CHECK: hlfir.where {
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_17:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK: %[[VAL_19:.*]] = fir.call @_QPlogical_func1() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_17]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: %[[VAL_24:.*]] = hlfir.exactly_once : f32 {
! CHECK: %[[VAL_28:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: }
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_35:.*]] = fir.call @_QPreal_func2() fastmath<contract> : () -> f32
! CHECK: %[[VAL_36:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: ^bb0(%[[VAL_37:.*]]: index):
! CHECK: %[[VAL_38:.*]] = hlfir.apply %[[VAL_28]], %[[VAL_37]] : (!hlfir.expr<?xf32>, index) -> f32
! CHECK: %[[VAL_39:.*]] = arith.addf %[[VAL_38]], %[[VAL_35]] fastmath<contract> : f32
! CHECK: hlfir.yield_element %[[VAL_39]] : f32
! CHECK: }
! CHECK: %[[VAL_41:.*]] = fir.call @_QPreal_func
! CHECK: hlfir.yield %[[VAL_41]] : f32 cleanup {
! CHECK: hlfir.destroy %[[VAL_36]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_28]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: }
! CHECK: %[[VAL_45:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK-NOT: hlfir.exactly_once
! CHECK: }
! CHECK: %[[VAL_53:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: fir.call @_QPelem_func
! CHECK: }
! CHECK: %[[VAL_57:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK: hlfir.yield %[[VAL_57]] : !hlfir.expr<?xf32> cleanup {
! CHECK: hlfir.destroy %[[VAL_57]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_53]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_45]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: } to {
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: hlfir.elsewhere mask {
! CHECK: %[[VAL_62:.*]] = hlfir.exactly_once : !hlfir.expr<100x!fir.logical<4>> {
! CHECK: %[[VAL_72:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK: fir.call @_QPlogical_func2() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_72]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: }
! CHECK: hlfir.yield %[[VAL_62]] : !hlfir.expr<100x!fir.logical<4>>
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: } to {
! CHECK: %[[VAL_80:.*]] = hlfir.exactly_once : i32 {
! CHECK: %[[VAL_81:.*]] = fir.call @_QPifoo() fastmath<contract> : () -> i32
! CHECK: hlfir.yield %[[VAL_81]] : i32
! CHECK: }
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK: return
! CHECK: }

subroutine test_where_in_forall(a, b, c)
real, dimension(:, :) :: a, b, c
interface
pure function pure_logical_func1()
logical :: pure_logical_func1(100)
end function
pure function pure_logical_func2()
logical :: pure_logical_func2(100)
end function
real pure elemental function pure_elem_func(x)
real, intent(in) :: x
end function
integer pure function pure_ifoo()
end function
end interface
forall(i=1:10)
where (pure_logical_func1())
a(2*i, :) = b(i, :) + pure_real_func(a(i,:)+b(i,:)+pure_real_func2()) + pure_elem_func(a(i,:))
elsewhere(pure_logical_func2())
a(2*i, 1:pure_ifoo()) = c(i, :)
end where
end forall
end subroutine
! CHECK-LABEL: func.func @_QPtest_where_in_forall(
! CHECK: hlfir.forall lb {
! CHECK: hlfir.yield %{{.*}} : i32
! CHECK: } ub {
! CHECK: hlfir.yield %{{.*}} : i32
! CHECK: } (%[[VAL_10:.*]]: i32) {
! CHECK: %[[VAL_11:.*]] = hlfir.forall_index "i" %[[VAL_10]] : (i32) -> !fir.ref<i32>
! CHECK: hlfir.where {
! CHECK: %[[VAL_21:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_23:.*]] = fir.call @_QPpure_logical_func1() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_21]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: %[[VAL_41:.*]] = hlfir.designate
! CHECK: %[[VAL_42:.*]] = hlfir.exactly_once : f32 {
! CHECK: hlfir.designate
! CHECK: hlfir.designate
! CHECK: %[[VAL_71:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_78:.*]] = fir.call @_QPpure_real_func2() fastmath<contract> : () -> f32
! CHECK: %[[VAL_79:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK: %[[VAL_84:.*]] = fir.call @_QPpure_real_func(
! CHECK: hlfir.yield %[[VAL_84]] : f32 cleanup {
! CHECK: hlfir.destroy %[[VAL_79]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_71]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: }
! CHECK: %[[VAL_85:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK-NOT: hlfir.exactly_once
! CHECK: %[[VAL_104:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: ^bb0(%[[VAL_105:.*]]: index):
! CHECK-NOT: hlfir.exactly_once
! CHECK: fir.call @_QPpure_elem_func
! CHECK: }
! CHECK: %[[VAL_108:.*]] = hlfir.elemental %{{.*}} unordered : (!fir.shape<1>) -> !hlfir.expr<?xf32> {
! CHECK: arith.addf
! CHECK: }
! CHECK: hlfir.yield %[[VAL_108]] : !hlfir.expr<?xf32> cleanup {
! CHECK: hlfir.destroy %[[VAL_108]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_104]] : !hlfir.expr<?xf32>
! CHECK: hlfir.destroy %[[VAL_85]] : !hlfir.expr<?xf32>
! CHECK: }
! CHECK: } to {
! CHECK: hlfir.designate
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: hlfir.elsewhere mask {
! CHECK: %[[VAL_129:.*]] = hlfir.exactly_once : !hlfir.expr<100x!fir.logical<4>> {
! CHECK: %[[VAL_139:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
! CHECK: %[[VAL_141:.*]] = fir.call @_QPpure_logical_func2() fastmath<contract> : () -> !fir.array<100x!fir.logical<4>>
! CHECK: hlfir.yield %{{.*}} : !hlfir.expr<100x!fir.logical<4>> cleanup {
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_139]]) fastmath<contract> : (!fir.ref<i8>) -> ()
! CHECK: }
! CHECK: }
! CHECK: hlfir.yield %[[VAL_129]] : !hlfir.expr<100x!fir.logical<4>>
! CHECK: } do {
! CHECK: hlfir.region_assign {
! CHECK: hlfir.designate
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: } to {
! CHECK: %[[VAL_165:.*]] = hlfir.exactly_once : i32 {
! CHECK: %[[VAL_166:.*]] = fir.call @_QPpure_ifoo() fastmath<contract> : () -> i32
! CHECK: hlfir.yield %[[VAL_166]] : i32
! CHECK: }
! CHECK: hlfir.designate
! CHECK: hlfir.yield %{{.*}} : !fir.box<!fir.array<?xf32>>
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK: }
! CHECK: return
! CHECK: }
10 changes: 10 additions & 0 deletions libc/hdr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ add_proxy_header_library(
libc.include.math
)

add_proxy_header_library(
errno_macros
HDRS
errno_macros.h
FULL_BUILD_DEPENDS
libc.include.errno
libc.include.llvm-libc-macros.error_number_macros
libc.include.llvm-libc-macros.generic_error_number_macros
)

add_proxy_header_library(
fcntl_macros
HDRS
Expand Down
28 changes: 28 additions & 0 deletions libc/hdr/errno_macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===-- Definition of macros from errno.h ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_HDR_ERRNO_MACROS_H
#define LLVM_LIBC_HDR_ERRNO_MACROS_H

#ifdef LIBC_FULL_BUILD

#ifdef __linux__
#include <linux/errno.h>

#include "llvm-libc-macros/error-number-macros.h"
#else // __linux__
#include "llvm-libc-macros/generic-error-number-macros.h"
#endif

#else // Overlay mode

#include <errno.h>

#endif // LLVM_LIBC_FULL_BUILD

#endif // LLVM_LIBC_HDR_ERRNO_MACROS_H
20 changes: 1 addition & 19 deletions libc/include/errno.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,11 @@

#include <linux/errno.h>

#ifndef ERFKILL
#define ERFKILL 132
#endif // ERFKILL

#ifndef EOWNERDEAD
#define EOWNERDEAD 130
#endif // EOWNERDEAD

#ifndef EHWPOISON
#define EHWPOISON 133
#endif // EHWPOISON

#ifndef ECANCELED
#define ECANCELED 125
#endif // ECANCELED

#ifndef ENOTSUP
#define ENOTSUP EOPNOTSUPP
#endif // ENOTSUP

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 131
#endif // ENOTRECOVERABLE
#include "llvm-libc-macros/linux/error-number-macros.h"

#else // __linux__
#include "llvm-libc-macros/generic-error-number-macros.h"
Expand Down
6 changes: 6 additions & 0 deletions libc/include/llvm-libc-macros/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ add_macro_header(
assert-macros.h
)

add_macro_header(
error_number_macros
HDR
error-number-macros.h
)

add_macro_header(
generic_error_number_macros
HDR
Expand Down
8 changes: 8 additions & 0 deletions libc/include/llvm-libc-macros/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef LLVM_LIBC_MACROS_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_ERROR_NUMBER_MACROS_H

#ifdef __linux__
#include "linux/error-number-macros.h"
#endif

#endif // LLVM_LIBC_MACROS_ERROR_NUMBER_MACROS_H
2 changes: 2 additions & 0 deletions libc/include/llvm-libc-macros/generic-error-number-macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@
#define EDOM 33
#define ERANGE 34
#define EILSEQ 35
#define ENAMETOOLONG 36
#define EOVERFLOW 75

#endif // LLVM_LIBC_MACROS_GENERIC_ERROR_NUMBER_MACROS_H
12 changes: 12 additions & 0 deletions libc/include/llvm-libc-macros/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mips)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sparc)

add_header(
error_number_macros
HDR
error-number-macros.h
DEPENDS
.mips.error_number_macros
.sparc.error_number_macros
)

add_header(
fcntl_macros
HDR
Expand Down
32 changes: 32 additions & 0 deletions libc/include/llvm-libc-macros/linux/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef LLVM_LIBC_MACROS_LINUX_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_ERROR_NUMBER_MACROS_H

#if defined(__mips__)
#include "mips/error-number-macros.h"

#elif defined(__sparc__)
#include "sparc/error-number-macros.h"

#else
#ifndef ECANCELED
#define ECANCELED 125
#endif // ECANCELED

#ifndef EOWNERDEAD
#define EOWNERDEAD 130
#endif // EOWNERDEAD

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 131
#endif // ENOTRECOVERABLE

#ifndef ERFKILL
#define ERFKILL 132
#endif // ERFKILL

#ifndef EHWPOISON
#define EHWPOISON 133
#endif // EHWPOISON
#endif

#endif // LLVM_LIBC_MACROS_LINUX_ERROR_NUMBER_MACROS_H
5 changes: 5 additions & 0 deletions libc/include/llvm-libc-macros/linux/mips/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_header(
error_number_macros
HDR
error-number-macros.h
)
24 changes: 24 additions & 0 deletions libc/include/llvm-libc-macros/linux/mips/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef LLVM_LIBC_MACROS_LINUX_MIPS_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_MIPS_ERROR_NUMBER_MACROS_H

#ifndef ECANCELED
#define ECANCELED 158
#endif // ECANCELED

#ifndef EOWNERDEAD
#define EOWNERDEAD 165
#endif // EOWNERDEAD

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 166
#endif // ENOTRECOVERABLE

#ifndef ERFKILL
#define ERFKILL 167
#endif // ERFKILL

#ifndef EHWPOISON
#define EHWPOISON 168
#endif // EHWPOISON

#endif // LLVM_LIBC_MACROS_LINUX_MIPS_ERROR_NUMBER_MACROS_H
5 changes: 5 additions & 0 deletions libc/include/llvm-libc-macros/linux/sparc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_header(
error_number_macros
HDR
error-number-macros.h
)
24 changes: 24 additions & 0 deletions libc/include/llvm-libc-macros/linux/sparc/error-number-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef LLVM_LIBC_MACROS_LINUX_SPARC_ERROR_NUMBER_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_SPARC_ERROR_NUMBER_MACROS_H

#ifndef ECANCELED
#define ECANCELED 127
#endif // ECANCELED

#ifndef EOWNERDEAD
#define EOWNERDEAD 132
#endif // EOWNERDEAD

#ifndef ENOTRECOVERABLE
#define ENOTRECOVERABLE 133
#endif // ENOTRECOVERABLE

#ifndef ERFKILL
#define ERFKILL 134
#endif // ERFKILL

#ifndef EHWPOISON
#define EHWPOISON 135
#endif // EHWPOISON

#endif // LLVM_LIBC_MACROS_LINUX_SPARC_ERROR_NUMBER_MACROS_H
2 changes: 1 addition & 1 deletion libc/src/__support/threads/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ add_header_library(
libc.src.__support.CPP.atomic
libc.src.__support.CPP.limits
libc.src.__support.CPP.optional
libc.hdr.types.struct_timespec
libc.src.__support.time.linux.abs_timeout
)

add_header_library(
Expand Down
11 changes: 4 additions & 7 deletions libc/src/__support/threads/linux/futex_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,20 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H

#include "hdr/types/struct_timespec.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/time/linux/abs_timeout.h"
#include <linux/errno.h>
#include <linux/futex.h>

namespace LIBC_NAMESPACE {
class Futex : public cpp::Atomic<FutexWordType> {
public:
struct Timeout {
timespec abs_time;
bool is_realtime;
};
using Timeout = internal::AbsTimeout;
LIBC_INLINE constexpr Futex(FutexWordType value)
: cpp::Atomic<FutexWordType>(value) {}
LIBC_INLINE Futex &operator=(FutexWordType value) {
Expand All @@ -37,7 +34,7 @@ class Futex : public cpp::Atomic<FutexWordType> {
bool is_shared = false) {
// use bitset variants to enforce abs_time
uint32_t op = is_shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE;
if (timeout && timeout->is_realtime) {
if (timeout && timeout->is_realtime()) {
op |= FUTEX_CLOCK_REALTIME;
}
for (;;) {
Expand All @@ -49,7 +46,7 @@ class Futex : public cpp::Atomic<FutexWordType> {
/* futex address */ this,
/* futex operation */ op,
/* expected value */ expected,
/* timeout */ timeout ? &timeout->abs_time : nullptr,
/* timeout */ timeout ? &timeout->get_timespec() : nullptr,
/* ignored */ nullptr,
/* bitset */ FUTEX_BITSET_MATCH_ANY);

Expand Down
33 changes: 30 additions & 3 deletions libc/src/__support/time/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
add_object_library(
add_header_library(
clock_gettime
HDRS
clock_gettime.h
SRCS
clock_gettime.cpp
DEPENDS
libc.include.sys_syscall
libc.hdr.types.struct_timespec
Expand All @@ -12,3 +10,32 @@ add_object_library(
libc.src.__support.error_or
libc.src.__support.OSUtil.osutil
)

add_header_library(
clock_conversion
HDRS
clock_conversion.h
DEPENDS
.clock_gettime
libc.src.__support.time.units
)

add_header_library(
abs_timeout
HDRS
abs_timeout.h
DEPENDS
libc.hdr.types.struct_timespec
libc.src.__support.time.units
libc.src.__support.CPP.expected
)

add_header_library(
monotonicity
HDRS
monotonicity.h
DEPENDS
.clock_conversion
.abs_timeout
libc.hdr.time_macros
)
Loading