How-to guides¶
Each guide below is pulled directly from a compiled, executed Catch2 test
(tagged [doc] in doc/snippets/), so it can’t drift out of sync with
the API it demonstrates – if a guide stops matching reality, the docs build
fails.
Add two float8 vectors¶
weaver::float8 behaves like a scalar float for the usual arithmetic
operators, but computes on 8 lanes at once:
weaver::float8 x{ 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
weaver::float8 y{ 8.0f, 7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
weaver::float8 z = x + y;
That single + isn’t sugar over a loop – it’s one SIMD instruction. The
function below is compiled and disassembled by the docs build itself
(doc/snippets/float8_asm.cpp); its operands arrive as parameters rather
than literals so the compiler can’t fold the addition away at compile time:
weaver::float8 add_float8(weaver::float8 a, weaver::float8 b)
{
return a + b;
}
Which the configured toolchain (here: whatever WEAVER_VECTORIZE this docs
build used) turns into:
; ymm0 = a, ymm1 = b
movaps xmm1,XMMWORD PTR [rsp+0x28]
movaps xmm0,XMMWORD PTR [rsp+0x38]
mov rax,rdi
addps xmm1,XMMWORD PTR [rsp+0x8]
addps xmm0,XMMWORD PTR [rsp+0x18]
movaps XMMWORD PTR [rdi],xmm1
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
The exact instructions depend on the compiler and target ISA – an AVX build
emits a single vaddps on 8-wide ymm registers, while an SSE build
splits the same 8 lanes across two 4-wide xmm registers instead. Either
way, this is the real output of the currently configured toolchain, not a
hand-picked example.
Pick a value without branching¶
FloatWeaver code has no real if: an if would have to pick one branch
for the whole vector, but different lanes can want different answers. Instead
you compute both possibilities and use select to keep, per lane,
whichever one the (also per-lane) condition asked for – the branchless
equivalent of a[i] < b[i] ? b[i] : a[i]:
weaver::float8 a{ 1.0f, 5.0f, 3.0f, 8.0f, 2.0f, 9.0f, 4.0f, 0.0f };
weaver::float8 b{ 4.0f, 2.0f, 6.0f, 1.0f, 7.0f, 3.0f, 5.0f, 9.0f };
// Equivalent to "for each lane: a[i] < b[i] ? b[i] : a[i]" -- but with no
// branch, so every lane is computed regardless of the others.
weaver::float8 m = weaver::select(a < b, b, a);
Same isolate-and-disassemble treatment as above
(doc/snippets/select_asm.cpp):
weaver::float8 select_max(weaver::float8 a, weaver::float8 b)
{
return weaver::select(a < b, b, a);
}
; ymm0 = a, ymm1 = b
movaps xmm2,XMMWORD PTR [rsp+0x8]
movaps xmm1,XMMWORD PTR [rsp+0x18]
mov rax,rdi
movaps xmm3,xmm2
movaps xmm0,xmm1
cmpltps xmm0,XMMWORD PTR [rsp+0x38]
cmpltps xmm3,XMMWORD PTR [rsp+0x28]
blendvps xmm1,XMMWORD PTR [rsp+0x38],xmm0
movdqa xmm0,xmm3
blendvps xmm2,XMMWORD PTR [rsp+0x28],xmm0
movaps XMMWORD PTR [rdi+0x10],xmm1
movaps XMMWORD PTR [rdi],xmm2
ret
Two instructions, no branch: a compare that produces a per-lane mask, then a
blend that picks lanes from a or b according to that mask – every
lane gets computed, and the “wrong” one is simply discarded rather than
skipped.
Take a square root¶
Not just + compiles down to a single instruction – most of the
<cmath>-style free functions in namespace weaver do too:
weaver::float8 x{ 4.0f, 9.0f, 16.0f, 25.0f, 1.0f, 100.0f, 64.0f, 36.0f };
weaver::float8 r = weaver::sqrt(x);
weaver::float8 sqrt_float8(weaver::float8 a)
{
return weaver::sqrt(a);
}
; ymm0 = a
sqrtps xmm1,XMMWORD PTR [rsp+0x8]
sqrtps xmm0,XMMWORD PTR [rsp+0x18]
mov rax,rdi
movaps XMMWORD PTR [rdi],xmm1
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
Approximate reciprocal and reciprocal square root¶
rcp and rsqrt trade a couple of ULPs of accuracy for hardware
approximation instructions that are cheaper than a real divide or square
root – useful when normalizing a vector or computing a rough inverse where
exactness doesn’t matter:
weaver::float8 x{ 2.0f, 4.0f, 8.0f, 5.0f, 1.0f, 10.0f, 16.0f, 20.0f };
// An approximation of `1 / x`, not an exact divide -- trades a couple of
// ULPs of accuracy for a much cheaper instruction.
weaver::float8 r = weaver::rcp(x);
weaver::float8 rcp_float8(weaver::float8 a)
{
return weaver::rcp(a);
}
; ymm0 = a
movaps xmm4,XMMWORD PTR [rsp+0x8]
pxor xmm2,xmm2
movaps xmm5,XMMWORD PTR [rsp+0x18]
mov rax,rdi
rcpps xmm1,xmm4
movaps xmm3,xmm4
cmpeqps xmm3,xmm2
cmpeqps xmm2,xmm5
movaps xmm0,xmm1
mulps xmm0,xmm1
addps xmm1,xmm1
mulps xmm0,xmm4
andnps xmm3,xmm0
rcpps xmm0,xmm5
subps xmm1,xmm3
movaps xmm4,xmm0
movaps XMMWORD PTR [rdi],xmm1
mulps xmm4,xmm0
addps xmm0,xmm0
mulps xmm4,xmm5
andnps xmm2,xmm4
subps xmm0,xmm2
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
weaver::float8 x{ 4.0f, 9.0f, 16.0f, 25.0f, 1.0f, 100.0f, 64.0f, 36.0f };
// An approximation of `1 / sqrt(x)` that avoids both the divide and the
// square root -- the common case for normalizing a vector.
weaver::float8 r = weaver::rsqrt(x);
weaver::float8 rsqrt_float8(weaver::float8 a)
{
return weaver::rsqrt(a);
}
; ymm0 = a
rsqrtps xmm2,XMMWORD PTR [rsp+0x8]
movss xmm0,DWORD PTR [rip+0x0]
rsqrtps xmm1,XMMWORD PTR [rsp+0x18]
mov rax,rdi
shufps xmm0,xmm0,0x0
movaps xmm3,xmm0
movaps xmm4,xmm2
mulps xmm4,xmm2
mulps xmm4,XMMWORD PTR [rsp+0x8]
subps xmm3,xmm4
movss xmm4,DWORD PTR [rip+0x0]
shufps xmm4,xmm4,0x0
mulps xmm2,xmm4
mulps xmm2,xmm3
movaps XMMWORD PTR [rdi],xmm2
movaps xmm2,xmm1
mulps xmm2,xmm1
mulps xmm2,XMMWORD PTR [rsp+0x18]
mulps xmm1,xmm4
subps xmm0,xmm2
mulps xmm0,xmm1
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
Branchless sign¶
sgn is a real library function, not a contrived example – it’s what a
branchless sign-without-abs costs in practice: a mask, an and, an or, and
an and:
weaver::float8 x{ -3.0f, 0.0f, 2.0f, -0.5f, 7.0f, -7.0f, 0.0f, 1.0f };
// -1 / 0 / 1 per lane, with no per-lane branch on the sign.
weaver::float8 s = weaver::sgn(x);
weaver::float8 sgn_float8(weaver::float8 a)
{
return weaver::sgn(a);
}
; ymm0 = a
pxor xmm0,xmm0
movaps xmm1,XMMWORD PTR [rsp+0x8]
mov rax,rdi
movss xmm4,DWORD PTR [rip+0x0]
movaps xmm2,xmm0
movss xmm3,DWORD PTR [rip+0x0]
cmpneqps xmm2,XMMWORD PTR [rsp+0x8]
shufps xmm4,xmm4,0x0
andps xmm1,xmm4
andps xmm4,XMMWORD PTR [rsp+0x18]
shufps xmm3,xmm3,0x0
orps xmm1,xmm3
andps xmm1,xmm2
movaps xmm2,xmm0
movaps xmm0,xmm4
cmpneqps xmm2,XMMWORD PTR [rsp+0x18]
orps xmm0,xmm3
movaps XMMWORD PTR [rdi],xmm1
andps xmm0,xmm2
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
Compare floats with a tolerance¶
Exact float equality is rarely what you want. weaver::equal implements a
relative-tolerance compare per lane – a realistic idiom, a few instructions
longer than a plain ==:
weaver::float8 a{ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
weaver::float8 b{ 1.0000001f, 1.1f, 0.9f, 1.0f, 2.0f, 1.00001f, 1.0f, 1.0f };
// A relative-tolerance compare, per lane -- see the reference this is
// based on in vector_types.h.
weaver::VectorScalar<bool, 8> close = weaver::equal(a, b, weaver::float8{ 1e-4f });
weaver::VectorScalar<bool, 8> equal_tol_float8(weaver::float8 a, weaver::float8 b, weaver::float8 tol)
{
return weaver::equal(a, b, tol);
}
; ymm0 = a, ymm1 = b, ymm2 = tol
movss xmm0,DWORD PTR [rip+0x0]
mov rax,rdi
shufps xmm0,xmm0,0x0
movaps xmm1,xmm0
movaps xmm2,xmm0
andnps xmm1,XMMWORD PTR [rsp+0x38]
andnps xmm2,XMMWORD PTR [rsp+0x18]
movaps xmm4,xmm0
movaps xmm3,xmm0
andnps xmm4,XMMWORD PTR [rsp+0x28]
andnps xmm3,XMMWORD PTR [rsp+0x8]
maxps xmm2,xmm1
movss xmm1,DWORD PTR [rip+0x0]
maxps xmm3,xmm4
shufps xmm1,xmm1,0x0
movaps xmm4,xmm1
maxps xmm1,xmm2
movaps xmm2,XMMWORD PTR [rsp+0x8]
subps xmm2,XMMWORD PTR [rsp+0x28]
mulps xmm1,XMMWORD PTR [rsp+0x58]
maxps xmm4,xmm3
movaps xmm3,xmm0
mulps xmm4,XMMWORD PTR [rsp+0x48]
andnps xmm3,xmm2
movaps xmm2,XMMWORD PTR [rsp+0x18]
subps xmm2,XMMWORD PTR [rsp+0x38]
andnps xmm0,xmm2
cmpleps xmm3,xmm4
cmpleps xmm0,xmm1
movaps XMMWORD PTR [rdi],xmm3
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
Check a condition across all lanes¶
Individual lanes can never branch, but FloatWeaver code is still allowed a
real if – as long as it branches on a single reduced bool, not on a
per-lane result. any/all/none are how a per-lane comparison
becomes that bool:
weaver::float8 x{ 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 20.0f };
weaver::float8 limit{ 9.0f };
// Per-lane comparisons don't decide anything by themselves -- `any`
// reduces the 8-wide result down to the single bool a real `if` needs.
if (weaver::any(x > limit))
{
// at least one lane exceeded the limit
}
bool any_greater_float8(weaver::float8 a, weaver::float8 b)
{
return weaver::any(a > b);
}
; ymm0 = a, ymm1 = b
movaps xmm1,XMMWORD PTR [rsp+0x38]
movaps xmm0,XMMWORD PTR [rsp+0x28]
cmpltps xmm1,XMMWORD PTR [rsp+0x18]
cmpltps xmm0,XMMWORD PTR [rsp+0x8]
movmskps eax,xmm1
movmskps edx,xmm0
shl eax,0x4
or eax,edx
setne al
ret
A compare, a movmsk to pull the per-lane mask bits into a scalar
register, then a plain integer test – this is the one place in these guides
where a scalar, not a vector, comes out the other end.
Horizontal reduction: dot product¶
Every example so far keeps 8 independent results, one per lane.
dot doesn’t: it collapses all 8 lanes of both operands into a single
float, so the instruction shape looks different from the rest of this
guide – a multiply-reduce followed by a lane-extraction step, not a
per-lane op:
weaver::float8 x{ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
weaver::float8 y{ 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f };
// Unlike the elementwise ops above, this collapses all 8 lanes into one
// plain `float` -- a horizontal reduction, not a per-lane result.
float d = x.dot(y);
float dot_float8(weaver::float8 a, weaver::float8 b)
{
return a.dot(b);
}
; ymm0 = a, ymm1 = b
movaps xmm1,XMMWORD PTR [rsp+0x18]
dpps xmm1,XMMWORD PTR [rsp+0x38],0xff
movaps xmm2,XMMWORD PTR [rsp+0x8]
dpps xmm2,XMMWORD PTR [rsp+0x28],0xff
movaps xmm0,xmm1
addss xmm0,xmm2
ret
Using FloatWeaver with Eigen¶
weaver::float4/float8/float16 work as the Scalar of an
Eigen::Matrix directly (see include/weaver/eigen.h) – no separate
vector type, no adapter code at the call site. The guides below show that
this isn’t just type-level plumbing: ordinary Eigen algorithms, written with
no knowledge of FloatWeaver, compile down to the same kind of vector
instructions as the weaver-native examples above.
Eigen vector arithmetic¶
The baseline case: an Eigen::Matrix<weaver::float8, 3, 1> is a
Vector3 holding 8 points at once, and operator+ works exactly as it
would for Eigen::Vector3f:
// weaver::float8 works as the Scalar of an Eigen::Matrix directly, no
// special vector type required -- this Vector3 holds 8 3D points.
Eigen::Matrix<weaver::float8, 3, 1> a{ weaver::float8{1.0f}, weaver::float8{2.0f}, weaver::float8{3.0f} };
Eigen::Matrix<weaver::float8, 3, 1> b{ weaver::float8{4.0f}, weaver::float8{5.0f}, weaver::float8{6.0f} };
Eigen::Matrix<weaver::float8, 3, 1> c = a + b;
Eigen::Matrix<weaver::float8, 3, 1> add_vec3_float8(
Eigen::Matrix<weaver::float8, 3, 1> a,
Eigen::Matrix<weaver::float8, 3, 1> b)
{
return a + b;
}
; rdi = return-value pointer, rsi = &a, rdx = &b
movaps xmm0,XMMWORD PTR [rdx+0x10]
movaps xmm1,XMMWORD PTR [rdx]
mov rax,rdi
addps xmm0,XMMWORD PTR [rsi+0x10]
addps xmm1,XMMWORD PTR [rsi]
movaps XMMWORD PTR [rdi+0x10],xmm0
movaps xmm0,XMMWORD PTR [rdx+0x30]
addps xmm0,XMMWORD PTR [rsi+0x30]
movaps XMMWORD PTR [rdi],xmm1
movaps xmm1,XMMWORD PTR [rdx+0x20]
addps xmm1,XMMWORD PTR [rsi+0x20]
movaps XMMWORD PTR [rdi+0x30],xmm0
movaps xmm0,XMMWORD PTR [rdx+0x50]
addps xmm0,XMMWORD PTR [rsi+0x50]
movaps XMMWORD PTR [rdi+0x20],xmm1
movaps xmm1,XMMWORD PTR [rdx+0x40]
addps xmm1,XMMWORD PTR [rsi+0x40]
movaps XMMWORD PTR [rdi+0x50],xmm0
movaps XMMWORD PTR [rdi+0x40],xmm1
ret
Three vaddps, one per vector component – proof the NumTraits/
numext wiring costs nothing over calling weaver::float8’s own
operator+ directly.
Squared norm and Eigen algorithms¶
squaredNorm() was never written for weaver::float8 – Eigen builds it
generically out of operator* and operator+, the same way it would for
any other Scalar:
Eigen::Matrix<weaver::float8, 3, 1> v{ weaver::float8{1.0f}, weaver::float8{2.0f}, weaver::float8{3.0f} };
// squaredNorm() is an Eigen algorithm, not a FloatWeaver primitive --
// nobody wrote it for this Scalar type, Eigen just calls operator* and
// operator+ generically.
weaver::float8 lenSq = v.squaredNorm();
weaver::float8 squared_norm_vec3_float8(Eigen::Matrix<weaver::float8, 3, 1> v)
{
return v.squaredNorm();
}
; rdi = &v
movaps xmm1,XMMWORD PTR [rsi+0x40]
movaps xmm0,XMMWORD PTR [rsi+0x50]
mov rax,rdi
movaps xmm5,XMMWORD PTR [rsi+0x20]
movaps xmm3,XMMWORD PTR [rsi+0x30]
mulps xmm1,xmm1
movaps xmm4,XMMWORD PTR [rsi]
movaps xmm2,XMMWORD PTR [rsi+0x10]
mulps xmm5,xmm5
mulps xmm3,xmm3
mulps xmm0,xmm0
mulps xmm4,xmm4
mulps xmm2,xmm2
addps xmm1,xmm5
addps xmm0,xmm3
addps xmm1,xmm4
addps xmm0,xmm2
movaps XMMWORD PTR [rdi],xmm1
movaps XMMWORD PTR [rdi+0x10],xmm0
ret
Cross product¶
cross() is the classic shuffle-and-multiply pattern – three multiplies
and three fused multiply-subtracts, still one screenful of assembly:
Eigen::Matrix<weaver::float8, 3, 1> a{ weaver::float8{1.0f}, weaver::float8{2.0f}, weaver::float8{3.0f} };
Eigen::Matrix<weaver::float8, 3, 1> b{ weaver::float8{4.0f}, weaver::float8{5.0f}, weaver::float8{6.0f} };
Eigen::Matrix<weaver::float8, 3, 1> c = a.cross(b);
Eigen::Matrix<weaver::float8, 3, 1> cross_vec3_float8(
Eigen::Matrix<weaver::float8, 3, 1> a,
Eigen::Matrix<weaver::float8, 3, 1> b)
{
return a.cross(b);
}
; rdi = return-value pointer, rsi = &a, rdx = &b
movaps xmm9,XMMWORD PTR [rsi+0x20]
movaps xmm1,XMMWORD PTR [rdx+0x20]
mov rax,rdi
movaps xmm11,XMMWORD PTR [rdx+0x40]
movaps xmm3,XMMWORD PTR [rsi+0x40]
movaps xmm12,xmm9
movaps xmm13,xmm1
movaps xmm7,XMMWORD PTR [rsi+0x30]
movaps xmm0,XMMWORD PTR [rdx+0x30]
mulps xmm13,xmm3
movaps xmm5,XMMWORD PTR [rdx]
movaps xmm4,XMMWORD PTR [rdx+0x10]
mulps xmm12,xmm11
movaps xmm10,XMMWORD PTR [rsi]
movaps xmm8,XMMWORD PTR [rsi+0x10]
movaps xmm6,XMMWORD PTR [rdx+0x50]
movaps xmm2,XMMWORD PTR [rsi+0x50]
mulps xmm3,xmm5
mulps xmm11,xmm10
mulps xmm1,xmm10
mulps xmm5,xmm9
subps xmm12,xmm13
movaps xmm13,xmm0
mulps xmm13,xmm2
mulps xmm0,xmm8
subps xmm3,xmm11
movaps XMMWORD PTR [rdi],xmm12
mulps xmm2,xmm4
movaps xmm12,xmm7
mulps xmm12,xmm6
subps xmm1,xmm5
mulps xmm6,xmm8
movaps XMMWORD PTR [rdi+0x20],xmm3
mulps xmm4,xmm7
movaps XMMWORD PTR [rdi+0x40],xmm1
subps xmm12,xmm13
subps xmm2,xmm6
subps xmm0,xmm4
movaps XMMWORD PTR [rdi+0x10],xmm12
movaps XMMWORD PTR [rdi+0x30],xmm2
movaps XMMWORD PTR [rdi+0x50],xmm0
ret
Branchless select over an Eigen vector¶
The same branchless pattern as “Pick a value without branching” above, but applied to a whole vector-valued quantity at once – e.g. choosing one of two candidate velocities for 8 particles in a single call, one blend per component:
// Two candidate velocities for 8 particles at once.
Eigen::Matrix<weaver::float8, 3, 1> fast{ weaver::float8{1.0f}, weaver::float8{2.0f}, weaver::float8{3.0f} };
Eigen::Matrix<weaver::float8, 3, 1> slow{ weaver::float8{4.0f}, weaver::float8{5.0f}, weaver::float8{6.0f} };
weaver::VectorScalar<bool, 8> useFast{ true, false, true, false, true, false, true, false };
// The same branchless pattern as "Pick a value without branching", but
// applied component-wise to a whole Eigen vector instead of one scalar.
Eigen::Matrix<weaver::float8, 3, 1> v = weaver::select(useFast, fast, slow);
Eigen::Matrix<weaver::float8, 3, 1> select_vec3_float8(
weaver::VectorScalar<bool, 8> mask,
Eigen::Matrix<weaver::float8, 3, 1> a,
Eigen::Matrix<weaver::float8, 3, 1> b)
{
return weaver::select(mask, a, b);
}
; ymm0 = mask, rdi = return-value pointer, rsi = &a, rdx = &b
pxor xmm1,xmm1
movaps xmm3,XMMWORD PTR [rdx+0x10]
movaps xmm4,XMMWORD PTR [rdx]
mov rax,rdi
movdqa xmm2,xmm1
pcmpgtd xmm1,XMMWORD PTR [rsp+0x8]
pcmpgtd xmm2,XMMWORD PTR [rsp+0x18]
movaps xmm0,xmm2
blendvps xmm3,XMMWORD PTR [rsi+0x10],xmm0
movaps xmm0,xmm1
blendvps xmm4,XMMWORD PTR [rsi],xmm0
movaps XMMWORD PTR [rdi+0x10],xmm3
movaps xmm3,XMMWORD PTR [rdx+0x30]
movaps xmm0,xmm2
movaps XMMWORD PTR [rdi],xmm4
movaps xmm5,XMMWORD PTR [rdx+0x20]
blendvps xmm3,XMMWORD PTR [rsi+0x30],xmm0
movaps xmm0,xmm1
blendvps xmm5,XMMWORD PTR [rsi+0x20],xmm0
movaps XMMWORD PTR [rdi+0x30],xmm3
movaps xmm6,XMMWORD PTR [rdx+0x50]
movaps xmm0,xmm2
movaps XMMWORD PTR [rdi+0x20],xmm5
movaps xmm3,XMMWORD PTR [rdx+0x40]
blendvps xmm6,XMMWORD PTR [rsi+0x50],xmm0
movaps xmm0,xmm1
blendvps xmm3,XMMWORD PTR [rsi+0x40],xmm0
movaps XMMWORD PTR [rdi+0x50],xmm6
movaps XMMWORD PTR [rdi+0x40],xmm3
ret
Merge axis-aligned boxes¶
This is closer to why FloatWeaver exists than a toy example:
Eigen::AlignedBox needs no changes at all to operate on 8 boxes
simultaneously. merged() takes the componentwise min of both boxes’
minimums and the max of their maximums:
// 8 independent 2D boxes, merged in one call -- Eigen::AlignedBox needs
// no changes to work with weaver::float8 as its Scalar.
Eigen::AlignedBox<weaver::float8, 2> a{
Eigen::Matrix<weaver::float8, 2, 1>{ weaver::float8{-1.0f}, weaver::float8{-1.0f} },
Eigen::Matrix<weaver::float8, 2, 1>{ weaver::float8{1.0f}, weaver::float8{1.0f} }
};
Eigen::AlignedBox<weaver::float8, 2> b{
Eigen::Matrix<weaver::float8, 2, 1>{ weaver::float8{0.0f}, weaver::float8{0.0f} },
Eigen::Matrix<weaver::float8, 2, 1>{ weaver::float8{2.0f}, weaver::float8{2.0f} }
};
Eigen::AlignedBox<weaver::float8, 2> merged = a.merged(b);
Eigen::AlignedBox<weaver::float8, 2> merge_box2_float8(
Eigen::AlignedBox<weaver::float8, 2> a,
Eigen::AlignedBox<weaver::float8, 2> b)
{
return a.merged(b);
}
; rdi = return-value pointer, rsi = &a, rdx = &b
movaps xmm1,XMMWORD PTR [rsi]
movaps xmm0,XMMWORD PTR [rsi+0x10]
mov rax,rdi
minps xmm1,XMMWORD PTR [rdx]
minps xmm0,XMMWORD PTR [rdx+0x10]
movaps XMMWORD PTR [rdi],xmm1
movaps xmm1,XMMWORD PTR [rsi+0x20]
minps xmm1,XMMWORD PTR [rdx+0x20]
movaps XMMWORD PTR [rdi+0x10],xmm0
movaps xmm0,XMMWORD PTR [rsi+0x30]
minps xmm0,XMMWORD PTR [rdx+0x30]
movaps XMMWORD PTR [rdi+0x20],xmm1
movaps xmm1,XMMWORD PTR [rsi+0x40]
maxps xmm1,XMMWORD PTR [rdx+0x40]
movaps XMMWORD PTR [rdi+0x30],xmm0
movaps xmm0,XMMWORD PTR [rsi+0x50]
maxps xmm0,XMMWORD PTR [rdx+0x50]
movaps XMMWORD PTR [rdi+0x40],xmm1
movaps xmm1,XMMWORD PTR [rsi+0x60]
maxps xmm1,XMMWORD PTR [rdx+0x60]
movaps XMMWORD PTR [rdi+0x50],xmm0
movaps xmm0,XMMWORD PTR [rsi+0x70]
maxps xmm0,XMMWORD PTR [rdx+0x70]
movaps XMMWORD PTR [rdi+0x60],xmm1
movaps XMMWORD PTR [rdi+0x70],xmm0
ret