|
| 1 | +// Array `==` / `!=` / `<` / `>` / `<=` / `>=` in @compute device code lower to the |
| 2 | +// druntime hooks core.internal.array.equality.__equals and |
| 3 | +// core.internal.array.comparison.__cmp (plus helpers like isEqual/at). Those hooks |
| 4 | +// live in host-only druntime modules but are device-legal. Regression guard: they |
| 5 | +// used to be either rejected during dcompute semantic analysis or emitted as hollow |
| 6 | +// `declare`-only stubs. This test asserts the device IR contains real `define`d |
| 7 | +// bodies (never bare `declare`s) for every element type that actually instantiates |
| 8 | +// the templates. |
| 9 | +// |
| 10 | +// float / double / real / float[N] / struct-with-opEquals / struct-with-float-field |
| 11 | +// all bypass the int[] memcmp fast path, so the genuine __equals / __cmp templates |
| 12 | +// are instantiated and codegen'd for the device. (Plain POD structs and integral |
| 13 | +// element types take the inline-memcmp path instead -- see compilable/dcompute_comparison_hooks.d.) |
| 14 | +// |
| 15 | +// REQUIRES: target_NVPTX |
| 16 | +// RUN: %ldc -mdcompute-targets=cuda-700 -m64 -output-ll -output-o -c \ |
| 17 | +// RUN: -mdcompute-file-prefix=comparison_hooks -Iinputs %s |
| 18 | +// RUN: FileCheck %s < comparison_hooks_cuda700_64.ll |
| 19 | + |
| 20 | +@compute(CompileFor.deviceOnly) module dcompute_comparison_hooks; |
| 21 | +import ldc.dcompute; |
| 22 | + |
| 23 | +struct SEq { int x; bool opEquals(ref const SEq o) const { return x == o.x; } } |
| 24 | +struct SFloat { float f; } |
| 25 | +struct SCmp { int x; int opCmp(ref const SCmp o) const { return x < o.x ? -1 : (x > o.x ? 1 : 0); } } |
| 26 | + |
| 27 | +// ---- __equals (== / !=) ---------------------------------------------------- |
| 28 | + |
| 29 | +// Dynamic float[] equality -> __equals!(float,float). |
| 30 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array8equality{{.*}}__equalsTfTf |
| 31 | +@kernel void eq_float(float[] a, float[] b, bool* o) { *o = (a == b); } |
| 32 | + |
| 33 | +// Dynamic double[] equality -> __equals!(double,double). |
| 34 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array8equality{{.*}}__equalsTdTd |
| 35 | +@kernel void eq_double(double[] a, double[] b, bool* o) { *o = (a == b); } |
| 36 | + |
| 37 | +// Static float[4] equality -> __equals!(float,float, len, len). |
| 38 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array8equality{{.*}}__equalsTfTfVmi4 |
| 39 | +@kernel void eq_static(float[4] a, float[4] b, bool* o) { *o = (a == b); } |
| 40 | + |
| 41 | +// Struct with custom opEquals -> __equals!(SEq,SEq), element-wise via the user opEquals. |
| 42 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array8equality{{.*}}__equalsTS{{.*}}3SEq |
| 43 | +@kernel void eq_opequals(SEq[] a, SEq[] b, bool* o) { *o = (a == b); } |
| 44 | + |
| 45 | +// Struct with a float field (forces element-wise compare, not memcmp) -> __equals!(SFloat,SFloat). |
| 46 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array8equality{{.*}}__equalsTS{{.*}}6SFloat |
| 47 | +@kernel void eq_floatfield(SFloat[] a, SFloat[] b, bool* o) { *o = (a == b); } |
| 48 | + |
| 49 | +// `!=` lowers to the same __equals!(float,float) hook (negated). No NEW symbol needed |
| 50 | +// (shares eq_float's instantiation), but it must compile and stay defined. |
| 51 | +@kernel void ne_float(float[] a, float[] b, bool* o) { *o = (a != b); } |
| 52 | + |
| 53 | +// The element comparator helper isEqual must also be defined (reached transitively |
| 54 | +// from __equals, lives in the same host-only equality module). |
| 55 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array8equality{{.*}}isEqual |
| 56 | + |
| 57 | +// ---- __cmp (< / > / <= / >=) ----------------------------------------------- |
| 58 | + |
| 59 | +// float[] ordering -> __cmp!float. All four relational operators lower to the |
| 60 | +// same __cmp!float hook, so one define covers <, >, <=, >=. |
| 61 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array10comparison{{.*}}__cmpTf |
| 62 | +@kernel void cmp_lt(float[] a, float[] b, bool* o) { *o = (a < b); } |
| 63 | +@kernel void cmp_gt(float[] a, float[] b, bool* o) { *o = (a > b); } |
| 64 | +@kernel void cmp_le(float[] a, float[] b, bool* o) { *o = (a <= b); } |
| 65 | +@kernel void cmp_ge(float[] a, float[] b, bool* o) { *o = (a >= b); } |
| 66 | + |
| 67 | +// double[] ordering -> __cmp!double. |
| 68 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array10comparison{{.*}}__cmpTd |
| 69 | +@kernel void cmp_double(double[] a, double[] b, bool* o) { *o = (a < b); } |
| 70 | + |
| 71 | +// int[] ordering -> __cmp!int (ordering goes through __cmp, NOT the memcmp fast path |
| 72 | +// that int[] `==` uses). |
| 73 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array10comparison{{.*}}__cmpTi |
| 74 | +@kernel void cmp_int(int[] a, int[] b, bool* o) { *o = (a < b); } |
| 75 | + |
| 76 | +// Struct with custom opCmp -> __cmp!(SCmp), element-wise via the user opCmp. |
| 77 | +// CHECK-DAG: define{{.*}}ptx_device{{.*}}@_D4core8internal5array10comparison{{.*}}__cmpTS{{.*}}4SCmp |
| 78 | +@kernel void cmp_opcmp(SCmp[] a, SCmp[] b, bool* o) { *o = (a < b); } |
| 79 | + |
| 80 | +// None of the hooks may be left as hollow `declare`-only stubs. |
| 81 | +// CHECK-NOT: declare{{.*}}__equals |
| 82 | +// CHECK-NOT: declare{{.*}}__cmp |
0 commit comments