ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
452 lines
12 KiB
D
452 lines
12 KiB
D
module core.internal.arrayop;
|
|
import core.internal.traits : Filter, Unqual;
|
|
|
|
version (GNU) version = GNU_OR_LDC;
|
|
version (LDC) version = GNU_OR_LDC;
|
|
|
|
/**
|
|
* Perform array (vector) operations and store the result in `res`. Operand
|
|
* types and operations are passed as template arguments in Reverse Polish
|
|
* Notation (RPN).
|
|
|
|
* Operands can be slices or scalar types. The unqualified element types of all
|
|
* slices must be `T`, scalar types must be implicitly convertible to `T`.
|
|
*
|
|
* Operations are encoded as strings, e.g. `"+"`, `"%"`, `"*="`. Unary
|
|
* operations are prefixed with "u", e.g. `"u-"`, `"u~"`. Only the last
|
|
* operation can and must be an assignment (`"="`) or op-assignment (`"op="`).
|
|
*
|
|
* All slice operands must have the same length as the result slice.
|
|
*
|
|
* Params: T[] = type of result slice
|
|
* Args = operand types and operations in RPN
|
|
* res = the slice in which to store the results
|
|
* args = operand values
|
|
*
|
|
* Returns: the slice containing the result
|
|
*/
|
|
T[] arrayOp(T : T[], Args...)(T[] res, Filter!(isType, Args) args) @trusted @nogc pure nothrow
|
|
{
|
|
enum check = opsSupported!(true, T, Filter!(not!isType, Args)); // must support all scalar ops
|
|
|
|
size_t pos;
|
|
static if (vectorizeable!(T[], Args))
|
|
{
|
|
alias vec = .vec!T;
|
|
alias load = .load!(T, vec.length);
|
|
alias store = .store!(T, vec.length);
|
|
|
|
// Given that there are at most as many scalars broadcast as there are
|
|
// operations in any `ary[] = ary[] op const op const`, it should always be
|
|
// worthwhile to choose vector operations.
|
|
if (res.length >= vec.length)
|
|
{
|
|
mixin(initScalarVecs!Args);
|
|
|
|
auto n = res.length / vec.length;
|
|
do
|
|
{
|
|
mixin(vectorExp!Args ~ ";");
|
|
pos += vec.length;
|
|
}
|
|
while (--n);
|
|
}
|
|
}
|
|
for (; pos < res.length; ++pos)
|
|
mixin(scalarExp!Args ~ ";");
|
|
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
|
|
// SIMD helpers
|
|
|
|
version (DigitalMars)
|
|
{
|
|
import core.simd;
|
|
|
|
template vec(T)
|
|
{
|
|
enum regsz = 16; // SSE2
|
|
enum N = regsz / T.sizeof;
|
|
alias vec = __vector(T[N]);
|
|
}
|
|
|
|
void store(T, size_t N)(T* p, in __vector(T[N]) val)
|
|
{
|
|
pragma(inline, true);
|
|
alias vec = __vector(T[N]);
|
|
|
|
static if (is(T == float))
|
|
cast(void) __simd_sto(XMM.STOUPS, *cast(vec*) p, val);
|
|
else static if (is(T == double))
|
|
cast(void) __simd_sto(XMM.STOUPD, *cast(vec*) p, val);
|
|
else
|
|
cast(void) __simd_sto(XMM.STODQU, *cast(vec*) p, val);
|
|
}
|
|
|
|
const(__vector(T[N])) load(T, size_t N)(in T* p)
|
|
{
|
|
import core.simd;
|
|
|
|
pragma(inline, true);
|
|
alias vec = __vector(T[N]);
|
|
|
|
static if (is(T == float))
|
|
return __simd(XMM.LODUPS, *cast(const vec*) p);
|
|
else static if (is(T == double))
|
|
return __simd(XMM.LODUPD, *cast(const vec*) p);
|
|
else
|
|
return __simd(XMM.LODDQU, *cast(const vec*) p);
|
|
}
|
|
|
|
__vector(T[N]) binop(string op, T, size_t N)(in __vector(T[N]) a, in __vector(T[N]) b)
|
|
{
|
|
pragma(inline, true);
|
|
return mixin("a " ~ op ~ " b");
|
|
}
|
|
|
|
__vector(T[N]) unaop(string op, T, size_t N)(in __vector(T[N]) a)
|
|
if (op[0] == 'u')
|
|
{
|
|
pragma(inline, true);
|
|
return mixin(op[1 .. $] ~ "a");
|
|
}
|
|
}
|
|
|
|
// mixin gen
|
|
|
|
// Check whether operations `ops` are supported for type `T`. Fails with a human-friendly static assert message, if `fail` is true.
|
|
template opsSupported(bool fail, T, ops...) if (ops.length > 1)
|
|
{
|
|
enum opsSupported = opsSupported!(fail, T, ops[0 .. $ / 2])
|
|
&& opsSupported!(fail, T, ops[$ / 2 .. $]);
|
|
}
|
|
|
|
template opsSupported(bool fail, T, string op)
|
|
{
|
|
static if (isUnaryOp(op))
|
|
{
|
|
enum opsSupported = is(typeof((T a) => mixin(op[1 .. $] ~ " a")));
|
|
static assert(!fail || opsSupported,
|
|
"Unary op `" ~ op[1 .. $] ~ "` not supported for element type " ~ T.stringof ~ ".");
|
|
}
|
|
else
|
|
{
|
|
enum opsSupported = is(typeof((T a, T b) => mixin("a " ~ op ~ " b")));
|
|
static assert(!fail || opsSupported,
|
|
"Binary op `" ~ op ~ "` not supported for element type " ~ T.stringof ~ ".");
|
|
}
|
|
}
|
|
|
|
// check whether slices have the unqualified element type `E` and scalars are implicitly convertible to `E`
|
|
// i.e. filter out things like float[] = float[] / size_t[]
|
|
enum compatibleVecTypes(E, T : T[]) = is(Unqual!T == Unqual!E); // array elem types must be same (maybe add cvtpi2ps)
|
|
enum compatibleVecTypes(E, T) = is(T : E); // scalar must be convertible to target elem type
|
|
enum compatibleVecTypes(E, Types...) = compatibleVecTypes!(E, Types[0 .. $ / 2])
|
|
&& compatibleVecTypes!(E, Types[$ / 2 .. $]);
|
|
|
|
version (GNU_OR_LDC)
|
|
{
|
|
// leave it to the auto-vectorizer
|
|
enum vectorizeable(E : E[], Args...) = false;
|
|
}
|
|
else
|
|
{
|
|
// check whether arrayOp is vectorizable
|
|
template vectorizeable(E : E[], Args...)
|
|
{
|
|
static if (is(vec!E))
|
|
enum vectorizeable = opsSupported!(false, vec!E, Filter!(not!isType, Args))
|
|
&& compatibleVecTypes!(E, Filter!(isType, Args));
|
|
else
|
|
enum vectorizeable = false;
|
|
}
|
|
|
|
version (X86_64) unittest
|
|
{
|
|
static assert(vectorizeable!(double[], const(double)[], double[], "+", "="));
|
|
static assert(!vectorizeable!(double[], const(ulong)[], double[], "+", "="));
|
|
}
|
|
}
|
|
|
|
bool isUnaryOp(string op)
|
|
{
|
|
return op[0] == 'u';
|
|
}
|
|
|
|
bool isBinaryOp(string op)
|
|
{
|
|
if (op == "^^")
|
|
return true;
|
|
if (op.length != 1)
|
|
return false;
|
|
switch (op[0])
|
|
{
|
|
case '+', '-', '*', '/', '%', '|', '&', '^':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool isBinaryAssignOp(string op)
|
|
{
|
|
return op.length >= 2 && op[$ - 1] == '=' && isBinaryOp(op[0 .. $ - 1]);
|
|
}
|
|
|
|
// Generate mixin expression to perform scalar arrayOp loop expression, assumes
|
|
// `pos` to be the current slice index, `args` to contain operand values, and
|
|
// `res` the target slice.
|
|
string scalarExp(Args...)()
|
|
{
|
|
string[] stack;
|
|
size_t argsIdx;
|
|
foreach (i, arg; Args)
|
|
{
|
|
static if (is(arg == T[], T))
|
|
stack ~= "args[" ~ argsIdx++.toString ~ "][pos]";
|
|
else static if (is(arg))
|
|
stack ~= "args[" ~ argsIdx++.toString ~ "]";
|
|
else static if (isUnaryOp(arg))
|
|
{
|
|
auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
|
|
stack[$ - 1] = op ~ stack[$ - 1];
|
|
}
|
|
else static if (arg == "=")
|
|
{
|
|
stack[$ - 1] = "res[pos] = cast(T)(" ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (isBinaryAssignOp(arg))
|
|
{
|
|
stack[$ - 1] = "res[pos] " ~ arg ~ " cast(T)(" ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (isBinaryOp(arg))
|
|
{
|
|
stack[$ - 2] = "(cast(T)(" ~ stack[$ - 2] ~ " " ~ arg ~ " " ~ stack[$ - 1] ~ "))";
|
|
stack.length -= 1;
|
|
}
|
|
else
|
|
assert(0, "Unexpected op " ~ arg);
|
|
}
|
|
assert(stack.length == 1);
|
|
return stack[0];
|
|
}
|
|
|
|
// Generate mixin statement to perform vector loop initialization, assumes
|
|
// `args` to contain operand values.
|
|
string initScalarVecs(Args...)()
|
|
{
|
|
size_t scalarsIdx;
|
|
string res;
|
|
foreach (aidx, arg; Args)
|
|
{
|
|
static if (is(arg == T[], T))
|
|
{
|
|
}
|
|
else static if (is(arg))
|
|
res ~= "immutable vec scalar" ~ scalarsIdx++.toString ~ " = args["
|
|
~ aidx.toString ~ "];\n";
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// Generate mixin expression to perform vector arrayOp loop expression, assumes
|
|
// `pos` to be the current slice index, `args` to contain operand values, and
|
|
// `res` the target slice.
|
|
string vectorExp(Args...)()
|
|
{
|
|
size_t scalarsIdx, argsIdx;
|
|
string[] stack;
|
|
foreach (i, arg; Args)
|
|
{
|
|
static if (is(arg == T[], T))
|
|
stack ~= "load(&args[" ~ argsIdx++.toString ~ "][pos])";
|
|
else static if (is(arg))
|
|
{
|
|
++argsIdx;
|
|
stack ~= "scalar" ~ scalarsIdx++.toString;
|
|
}
|
|
else static if (isUnaryOp(arg))
|
|
{
|
|
auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
|
|
stack[$ - 1] = "unaop!\"" ~ arg ~ "\"(" ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (arg == "=")
|
|
{
|
|
stack[$ - 1] = "store(&res[pos], " ~ stack[$ - 1] ~ ")";
|
|
}
|
|
else static if (isBinaryAssignOp(arg))
|
|
{
|
|
stack[$ - 1] = "store(&res[pos], binop!\"" ~ arg[0 .. $ - 1]
|
|
~ "\"(load(&res[pos]), " ~ stack[$ - 1] ~ "))";
|
|
}
|
|
else static if (isBinaryOp(arg))
|
|
{
|
|
stack[$ - 2] = "binop!\"" ~ arg ~ "\"(" ~ stack[$ - 2] ~ ", " ~ stack[$ - 1] ~ ")";
|
|
stack.length -= 1;
|
|
}
|
|
else
|
|
assert(0, "Unexpected op " ~ arg);
|
|
}
|
|
assert(stack.length == 1);
|
|
return stack[0];
|
|
}
|
|
|
|
// other helpers
|
|
|
|
enum isType(T) = true;
|
|
enum isType(alias a) = false;
|
|
template not(alias tmlp)
|
|
{
|
|
enum not(Args...) = !tmlp!Args;
|
|
}
|
|
|
|
string toString(size_t num)
|
|
{
|
|
import core.internal.string : unsignedToTempString;
|
|
|
|
char[20] buf = void;
|
|
return unsignedToTempString(num, buf).idup;
|
|
}
|
|
|
|
bool contains(T)(in T[] ary, in T[] vals...)
|
|
{
|
|
foreach (v1; ary)
|
|
foreach (v2; vals)
|
|
if (v1 == v2)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// tests
|
|
|
|
version (unittest) template TT(T...)
|
|
{
|
|
alias TT = T;
|
|
}
|
|
|
|
version (unittest) template _arrayOp(Args...)
|
|
{
|
|
alias _arrayOp = arrayOp!Args;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static void check(string op, TA, TB, T, size_t N)(TA a, TB b, in ref T[N] exp)
|
|
{
|
|
T[N] res;
|
|
_arrayOp!(T[], TA, TB, op, "=")(res[], a, b);
|
|
foreach (i; 0 .. N)
|
|
assert(res[i] == exp[i]);
|
|
}
|
|
|
|
static void check2(string unaOp, string binOp, TA, TB, T, size_t N)(TA a, TB b, in ref T[N] exp)
|
|
{
|
|
T[N] res;
|
|
_arrayOp!(T[], TA, TB, unaOp, binOp, "=")(res[], a, b);
|
|
foreach (i; 0 .. N)
|
|
assert(res[i] == exp[i]);
|
|
}
|
|
|
|
static void test(T, string op, size_t N = 16)(T a, T b, T exp)
|
|
{
|
|
T[N] va = a, vb = b, vexp = exp;
|
|
|
|
check!op(va[], vb[], vexp);
|
|
check!op(va[], b, vexp);
|
|
check!op(a, vb[], vexp);
|
|
}
|
|
|
|
static void test2(T, string unaOp, string binOp, size_t N = 16)(T a, T b, T exp)
|
|
{
|
|
T[N] va = a, vb = b, vexp = exp;
|
|
|
|
check2!(unaOp, binOp)(va[], vb[], vexp);
|
|
check2!(unaOp, binOp)(va[], b, vexp);
|
|
check2!(unaOp, binOp)(a, vb[], vexp);
|
|
}
|
|
|
|
alias UINTS = TT!(ubyte, ushort, uint, ulong);
|
|
alias INTS = TT!(byte, short, int, long);
|
|
alias FLOATS = TT!(float, double);
|
|
|
|
foreach (T; TT!(UINTS, INTS, FLOATS))
|
|
{
|
|
test!(T, "+")(1, 2, 3);
|
|
test!(T, "-")(3, 2, 1);
|
|
static if (__traits(compiles, { import std.math; }))
|
|
test!(T, "^^")(2, 3, 8);
|
|
|
|
test2!(T, "u-", "+")(3, 2, 1);
|
|
}
|
|
|
|
foreach (T; TT!(UINTS, INTS))
|
|
{
|
|
test!(T, "|")(1, 2, 3);
|
|
test!(T, "&")(3, 1, 1);
|
|
test!(T, "^")(3, 1, 2);
|
|
|
|
test2!(T, "u~", "+")(3, cast(T)~2, 5);
|
|
}
|
|
|
|
foreach (T; TT!(INTS, FLOATS))
|
|
{
|
|
test!(T, "-")(1, 2, -1);
|
|
test2!(T, "u-", "+")(-3, -2, -1);
|
|
test2!(T, "u-", "*")(-3, -2, -6);
|
|
}
|
|
|
|
foreach (T; TT!(UINTS, INTS, FLOATS))
|
|
{
|
|
test!(T, "*")(2, 3, 6);
|
|
test!(T, "/")(8, 4, 2);
|
|
test!(T, "%")(8, 6, 2);
|
|
}
|
|
}
|
|
|
|
// test handling of v op= exp
|
|
unittest
|
|
{
|
|
uint[32] c;
|
|
arrayOp!(uint[], uint, "+=")(c[], 2);
|
|
foreach (v; c)
|
|
assert(v == 2);
|
|
static if (__traits(compiles, { import std.math; }))
|
|
{
|
|
arrayOp!(uint[], uint, "^^=")(c[], 3);
|
|
foreach (v; c)
|
|
assert(v == 8);
|
|
}
|
|
}
|
|
|
|
// proper error message for UDT lacking certain ops
|
|
unittest
|
|
{
|
|
static assert(!is(typeof(&arrayOp!(int[4][], int[4], "+="))));
|
|
static assert(!is(typeof(&arrayOp!(int[4][], int[4], "u-", "="))));
|
|
|
|
static struct S
|
|
{
|
|
}
|
|
|
|
static assert(!is(typeof(&arrayOp!(S[], S, "+="))));
|
|
static assert(!is(typeof(&arrayOp!(S[], S[], "*", S, "+="))));
|
|
static struct S2
|
|
{
|
|
S2 opBinary(string op)(in S2) @nogc pure nothrow
|
|
{
|
|
return this;
|
|
}
|
|
|
|
ref S2 opOpAssign(string op)(in S2) @nogc pure nothrow
|
|
{
|
|
return this;
|
|
}
|
|
}
|
|
|
|
static assert(is(typeof(&arrayOp!(S2[], S2[], S2[], S2, "*", "+", "="))));
|
|
static assert(is(typeof(&arrayOp!(S2[], S2[], S2, "*", "+="))));
|
|
}
|