import std.cpuid; import std.stdio; //----------------------------------------------------------------------------- // This code goes into the runtime library const uint CPU_NO_EXTENSION = 0, CPU_MMX = 1, CPU_SSE = 2, CPU_SSE2 = 4, CPU_SSE3 = 8; /****************************************************************************** A function pointer with a bitmask for it's required extensions ******************************************************************************/ struct MultiTargetVariant { static MultiTargetVariant opCall(uint ext, void* func) { MultiTargetVariant mtv; mtv.ext = ext; mtv.func = func; return mtv; } uint ext; void* func; } /****************************************************************************** Chooses the first matching MTV and saves it's FP to the dummy entry in the VTBL ******************************************************************************/ void LinkMultiTarget(ClassInfo ci, void* dummy_ptr, MultiTargetVariant[] multi_target_variants) { uint extensions; if ( mmx ) extensions |= CPU_MMX; if ( sse ) extensions |= CPU_SSE; if ( sse2 ) extensions |= CPU_SSE2; if ( sse3 ) extensions |= CPU_SSE3; foreach ( i, inout vp; ci.vtbl ) { if ( vp is dummy_ptr ) { foreach ( variant; multi_target_variants ) { if ( (variant.ext & extensions) == variant.ext ) { vp = variant.func; break; } } assert(vp !is dummy_ptr); break; } } } //----------------------------------------------------------------------------- // This is application code /****************************************************************************** A class with a multi-target function ******************************************************************************/ class MyMultiTargetClass { // The following 3 functions could be generated automatically by the compiler // with different targets enabled. For example, when we have language support for // vector operations, the compiler could generate multiple versions for different // SIMD extensions. Then there would be only one extension independent implementation. char[] multi_target_sse2() { return "using SSE2"; } char[] multi_target_sse_mmx() { return "using SSE and MMX"; } char[] multi_target_noext() { return "using no extension"; } // The following code could be generated by the compiler if there are multi-target // functions char[] multi_target() { return null; } static this() { MultiTargetVariant[] variants = [ MultiTargetVariant(CPU_SSE2, &multi_target_sse2), MultiTargetVariant(CPU_SSE|CPU_MMX, &multi_target_sse_mmx), MultiTargetVariant(CPU_NO_EXTENSION, &multi_target_noext) ]; LinkMultiTarget(this.classinfo, &multi_target, variants); } } /****************************************************************************** Finally, the usage is completely opaque and there is no runtime overhead besides the detection at startup. ******************************************************************************/ void main() { MyMultiTargetClass t = new MyMultiTargetClass; writefln("%s", t.multi_target); }