Rename memory.op.zero into fill

- Rename memory.op.zero to fill, which accepts one template parameter: one
byte to fill the memory with.
- Fix bug on x86_64: RAX (register keeping the value to fill with) isn't set if
the pointer was already aligned.
This commit is contained in:
Eugen Wissner 2017-08-06 06:22:28 +02:00
parent ed92e3993e
commit 2934bb16d7
2 changed files with 76 additions and 38 deletions

View File

@ -12,6 +12,8 @@
*/ */
module tanya.memory.arch.x86_64; module tanya.memory.arch.x86_64;
import tanya.memory.op;
version (D_InlineAsm_X86_64): version (D_InlineAsm_X86_64):
pragma(inline, true) 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) pragma(inline, true)
package (tanya.memory) void zero(void[] memory) package (tanya.memory) void fill(ubyte Byte)(void[] memory)
pure nothrow @system @nogc pure nothrow @system @nogc
{ {
asm pure nothrow @nogc asm pure nothrow @nogc
@ -94,7 +110,6 @@ pure nothrow @system @nogc
* RCX - array. * RCX - array.
*/ */
mov R8, [ RCX ]; mov R8, [ RCX ];
mov R9, [ RCX + 8 ];
} }
else asm pure nothrow @nogc else asm pure nothrow @nogc
{ {
@ -103,53 +118,55 @@ pure nothrow @system @nogc
* RDI - length. * RDI - length.
*/ */
mov R8, RDI; mov R8, RDI;
mov R9, RSI;
} }
mixin(MovArrayPointer!"R9");
asm pure nothrow @nogc asm pure nothrow @nogc
{ {
// Check for zero length. // Check for zero length.
test R8, R8; test R8, R8;
jz end; jz end;
}
// Set to 0. // 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; 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. // Check if the pointer is aligned to a 16-byte boundary.
and R9, -0x10; and R9, -0x10;
} }
// Compute the number of misaligned bytes. // Compute the number of misaligned bytes.
version (Windows) asm pure nothrow @nogc mixin(MovArrayPointer!"R10");
{
mov RAX, [ RCX + 8 ];
}
else asm pure nothrow @nogc
{
mov RAX, RSI;
}
asm pure nothrow @nogc asm pure nothrow @nogc
{ {
sub RAX, R9; sub R10, R9;
test RAX, RAX; test R10, R10;
jz aligned; jz aligned;
// Get the number of bytes to be written until we are aligned. // Get the number of bytes to be written until we are aligned.
mov RDX, 0x10; mov RDX, 0x10;
sub RDX, RAX; sub RDX, R10;
}
version (Windows) asm pure nothrow @nogc
{
mov R9, [ RCX + 8 ];
}
else asm pure nothrow @nogc
{
mov R9, RSI;
} }
mixin(MovArrayPointer!"R9");
asm pure nothrow @nogc asm pure nothrow @nogc
{ {
// Set RAX to zero, so we can set bytes and dwords.
xor RAX, RAX;
naligned: naligned:
mov [ R9 ], AL; // Write a byte. mov [ R9 ], AL; // Write a byte.

View File

@ -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: * Param:
* Byte = The value to fill $(D_PARAM memory) with.
* memory = Memory block. * 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) version (D_InlineAsm_X86_64)
{ {
tanya.memory.arch.x86_64.zero(memory); tanya.memory.arch.x86_64.fill!Byte(memory);
} }
else // Naive implementation. else // Naive implementation.
{ {
@ -133,7 +149,7 @@ void zero(void[] memory) pure nothrow @trusted @nogc
// Align. // Align.
while (((cast(size_t) vp) & alignmentMask) != 0) while (((cast(size_t) vp) & alignmentMask) != 0)
{ {
*vp++ = 0; *vp++ = Byte;
--n; --n;
} }
@ -141,7 +157,7 @@ void zero(void[] memory) pure nothrow @trusted @nogc
auto sp = cast(size_t*) vp; auto sp = cast(size_t*) vp;
while (n / size_t.sizeof > 0) while (n / size_t.sizeof > 0)
{ {
*sp++ = 0; *sp++ = FilledBytes!Byte;
n -= size_t.sizeof; n -= size_t.sizeof;
} }
@ -149,7 +165,7 @@ void zero(void[] memory) pure nothrow @trusted @nogc
vp = cast(ubyte*) sp; vp = cast(ubyte*) sp;
while (n--) while (n--)
{ {
*vp = 0; *vp = Byte;
++vp; ++vp;
} }
} }
@ -159,14 +175,14 @@ void zero(void[] memory) pure nothrow @trusted @nogc
pure nothrow @safe @nogc unittest pure nothrow @safe @nogc unittest
{ {
ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9]; ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
memory.zero(); memory.fill!0();
foreach (ubyte v; memory) foreach (ubyte v; memory)
{ {
assert(v == 0); 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. // lengths.
pure nothrow @safe @nogc private unittest pure nothrow @safe @nogc private unittest
{ {
@ -178,10 +194,15 @@ pure nothrow @safe @nogc private unittest
{ {
v = i; v = i;
} }
zero(memory[j .. $]); fill(memory[j .. $]);
foreach (ubyte v; memory[j .. $]) foreach (ubyte v; memory[j .. $])
{ {
assert(v == 0); assert(v == 0);
} }
fill!1(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 1);
}
} }
} }