diff --git a/source/tanya/memory/arch/x86_64.d b/source/tanya/memory/arch/x86_64.d index 2db1aaa..a9caec5 100644 --- a/source/tanya/memory/arch/x86_64.d +++ b/source/tanya/memory/arch/x86_64.d @@ -12,6 +12,8 @@ */ module tanya.memory.arch.x86_64; +import tanya.memory.op; + version (D_InlineAsm_X86_64): pragma(inline, true) @@ -80,8 +82,22 @@ pure nothrow @system @nogc } } +private enum const(char[]) MovArrayPointer(string Destination)() +{ + string asmCode = "asm pure nothrow @nogc { mov "; + version (Windows) + { + asmCode ~= Destination ~ ", [ RCX + 8 ];"; + } + else + { + asmCode ~= Destination ~ ", RSI;"; + } + return asmCode ~ "}"; +} + pragma(inline, true) -package (tanya.memory) void zero(void[] memory) +package (tanya.memory) void fill(ubyte Byte)(void[] memory) pure nothrow @system @nogc { asm pure nothrow @nogc @@ -94,7 +110,6 @@ pure nothrow @system @nogc * RCX - array. */ mov R8, [ RCX ]; - mov R9, [ RCX + 8 ]; } else asm pure nothrow @nogc { @@ -103,53 +118,55 @@ pure nothrow @system @nogc * RDI - length. */ mov R8, RDI; - mov R9, RSI; } + mixin(MovArrayPointer!"R9"); + asm pure nothrow @nogc { // Check for zero length. test R8, R8; jz end; - - // Set to 0. - pxor XMM0, XMM0; - + } + // Set 128- and 64-bit registers to values we want to fill with. + static if (Byte == 0) + { + asm pure nothrow @nogc + { + xor RAX, RAX; + pxor XMM0, XMM0; + } + } + else + { + enum ulong FilledBytes = FilledBytes!Byte; + asm pure nothrow @nogc + { + mov RAX, FilledBytes; + movq XMM0, RAX; + movlhps XMM0, XMM0; + } + } + asm pure nothrow @nogc + { // Check if the pointer is aligned to a 16-byte boundary. and R9, -0x10; } // Compute the number of misaligned bytes. - version (Windows) asm pure nothrow @nogc - { - mov RAX, [ RCX + 8 ]; - } - else asm pure nothrow @nogc - { - mov RAX, RSI; - } + mixin(MovArrayPointer!"R10"); asm pure nothrow @nogc { - sub RAX, R9; + sub R10, R9; - test RAX, RAX; + test R10, R10; jz aligned; // Get the number of bytes to be written until we are aligned. mov RDX, 0x10; - sub RDX, RAX; - } - version (Windows) asm pure nothrow @nogc - { - mov R9, [ RCX + 8 ]; - } - else asm pure nothrow @nogc - { - mov R9, RSI; + sub RDX, R10; } + mixin(MovArrayPointer!"R9"); asm pure nothrow @nogc { - // Set RAX to zero, so we can set bytes and dwords. - xor RAX, RAX; - naligned: mov [ R9 ], AL; // Write a byte. diff --git a/source/tanya/memory/op.d b/source/tanya/memory/op.d index 25f7d25..e0a3dea 100644 --- a/source/tanya/memory/op.d +++ b/source/tanya/memory/op.d @@ -113,17 +113,33 @@ private pure nothrow @safe @nogc unittest } } +/* + * size_t value each of which bytes is set to `Byte`. + */ +package template FilledBytes(ubyte Byte, ubyte I = 0) +{ + static if (I == size_t.sizeof) + { + enum size_t FilledBytes = Byte; + } + else + { + enum size_t FilledBytes = (FilledBytes!(Byte, I + 1) << 8) | Byte; + } +} + /** - * Fills $(D_PARAM memory) with zero-valued bytes. + * Fills $(D_PARAM memory) with single $(D_PARAM Byte)s. * * Param: + * Byte = The value to fill $(D_PARAM memory) with. * memory = Memory block. */ -void zero(void[] memory) pure nothrow @trusted @nogc +void fill(ubyte Byte = 0)(void[] memory) pure nothrow @trusted @nogc { version (D_InlineAsm_X86_64) { - tanya.memory.arch.x86_64.zero(memory); + tanya.memory.arch.x86_64.fill!Byte(memory); } else // Naive implementation. { @@ -133,7 +149,7 @@ void zero(void[] memory) pure nothrow @trusted @nogc // Align. while (((cast(size_t) vp) & alignmentMask) != 0) { - *vp++ = 0; + *vp++ = Byte; --n; } @@ -141,7 +157,7 @@ void zero(void[] memory) pure nothrow @trusted @nogc auto sp = cast(size_t*) vp; while (n / size_t.sizeof > 0) { - *sp++ = 0; + *sp++ = FilledBytes!Byte; n -= size_t.sizeof; } @@ -149,7 +165,7 @@ void zero(void[] memory) pure nothrow @trusted @nogc vp = cast(ubyte*) sp; while (n--) { - *vp = 0; + *vp = Byte; ++vp; } } @@ -159,14 +175,14 @@ void zero(void[] memory) pure nothrow @trusted @nogc pure nothrow @safe @nogc unittest { ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - memory.zero(); + memory.fill!0(); foreach (ubyte v; memory) { assert(v == 0); } } -// Stress test. Checks that `zero` can handle unaligned pointers and different +// Stress test. Checks that `fill` can handle unaligned pointers and different // lengths. pure nothrow @safe @nogc private unittest { @@ -178,10 +194,15 @@ pure nothrow @safe @nogc private unittest { v = i; } - zero(memory[j .. $]); + fill(memory[j .. $]); foreach (ubyte v; memory[j .. $]) { assert(v == 0); } + fill!1(memory[j .. $]); + foreach (ubyte v; memory[j .. $]) + { + assert(v == 1); + } } }