Repository: ispc/ispc

Issue #2611: Efficient codegen for narrower register widths
Created: 2023-08-16T21:05:11Z
Consider the following code - https://ispc.godbolt.org/z/Gfre6sG3q
In the VectorMax example where "fake varying" is used, unneeded movs and inserts are being added.
When using foreach with loop counts less than target width, narrower register widths should be recognized in cases where they match. In the VectorMax1 example case, 128b xmm. Currently the compiler is spilling to stack and generating masked movs.
Both examples should generate this code:
.LBB1_3:                                # =>This Inner Loop Header: Depth=1
        vmovups xmm0, xmmword ptr [rsi + 4*rcx]
        vmaxps  xmm0, xmm0, xmmword ptr [rdx + 4*rcx]
        vmaxps  xmm0, xmm0, xmmword ptr [rdi + 4*rcx]
        vmovdqu xmmword ptr [rdi + 4*rcx], xmm0
        add     rcx, 4
        cmp     rcx, r9
        jl      .LBB1_3

Full code here:
#define EXPLICIT_VECTOR4 0

struct FVector4f
{
#if EXPLICIT_VECTOR4 == 0
	float V[4];
#else
	float<4> V;
#endif
};

/* Ideal codegen for EXPLICIT_VECTOR4 0:
.LBB1_3:                                # =>This Inner Loop Header: Depth=1
        vmovups xmm0, xmmword ptr [rsi + 4*rcx]
        vmaxps  xmm0, xmm0, xmmword ptr [rdx + 4*rcx]
        vmaxps  xmm0, xmm0, xmmword ptr [rdi + 4*rcx]
        vmovdqu xmmword ptr [rdi + 4*rcx], xmm0
        add     rcx, 4
        cmp     rcx, r9
        jl      .LBB1_3
*/

/* Ideal codegen for EXPLICIT_VECTOR4 1:
.LBB1_3:                                # =>This Inner Loop Header: Depth=1
        vmovaps xmm0, xmmword ptr [rsi + 4*rcx]
        vmaxps  xmm0, xmm0, xmmword ptr [rdx + 4*rcx]
        vmaxps  xmm0, xmm0, xmmword ptr [rdi + 4*rcx]
        vmovaps xmmword ptr [rdi + 4*rcx], xmm0
        add     rcx, 4
        cmp     rcx, r9
        jl      .LBB1_3
*/

// Extra movsxd, vmovd, vpinsrd after 2x vmaxps with EXPLICIT_VECTOR4 0.
inline uniform FVector4f VectorMax(const uniform FVector4f& V1, const uniform FVector4f& V2)
{
	varying float S0, S1, Result;
	*((uniform FVector4f *uniform)&S0) = *((uniform FVector4f *uniform)&V1);
	*((uniform FVector4f *uniform)&S1) = *((uniform FVector4f *uniform)&V2);

	Result = max(S0, S1);

	return *((uniform FVector4f *uniform)&Result);
}

export void Max_Uniform_FVector4f(uniform float A[], uniform float B[], uniform float C[], uniform int Num)
{
	for(uniform int i = 0; i < Num; i+=4)
	{
		uniform FVector4f *uniform pA = (uniform FVector4f *uniform)&A[i];
		uniform FVector4f *uniform pB = (uniform FVector4f *uniform)&B[i];
		uniform FVector4f *uniform pC = (uniform FVector4f *uniform)&C[i];

		*pA = VectorMax(VectorMax(*pB, *pC), *pA);
	}
}

inline uniform FVector4f VectorMax1(const uniform FVector4f& V1, const uniform FVector4f& V2)
{
	uniform FVector4f Result;

	foreach(i = 0 ... 4)
	{
		Result.V[i] = max(V1.V[i], V2.V[i]);
	}

	return Result;
}

// Unneeded stack and maskmov usage. Should be using xmm throughout.
export void Max1_Uniform_FVector4f(uniform float A[], uniform float B[], uniform float C[], uniform int Num)
{
	for(uniform int i = 0; i < Num; i+=4)
	{
		uniform FVector4f *uniform pA = (uniform FVector4f *uniform)&A[i];
		uniform FVector4f *uniform pB = (uniform FVector4f *uniform)&B[i];
		uniform FVector4f *uniform pC = (uniform FVector4f *uniform)&C[i];

		*pA = VectorMax1(VectorMax1(*pB, *pC), *pA);
	}
}

Duplicate issue #2719: Masked stores used when setting shorter width registers in some cases
Created: 2023-12-15T13:44:13Z
Given the following code: https://godbolt.org/z/Kraa491fn
ISPC will operate on XMM registers up until the foreach() is encountered. Since the result is available in XMM0, it can safely perform an aligned/unaligned store using that register.
ISPC seems to broadcast to YMM0, load YMM1, mask store YMM0, and then clear the upper bits on YMM0.

Closing comment (2024-04-25T01:37:09Z): Closing as duplicate of #2611.  This issue is mentioned in the other issue's thread.