[libre-riscv-dev] Instruction sorta-prefixes for easier high-register access
Jacob Lifshay
programmerjake at gmail.com
Thu Jan 31 06:08:45 GMT 2019
On Wed, Jan 30, 2019, 20:34 Luke Kenneth Casson Leighton <lkcl at lkcl.net
wrote:
> ok so i thought about the vlpN concept, and if the register-prefixing
> encodes scalar/vector already, then reserving '0b00 for "scalar" is
> redundant. it would therefore be better to split out VL from
> predicate specs.
>
I disagree, having a combined field allows using the otherwise-reserved
predicate of "never" to encode other less common VL-multipliers.
predicate:
000: x0 (never)
001: pr1
010: pr2
011: pr3
100: ~x0 (always)
101: ~pr1
110: ~pr2
111: ~pr3
>
> there is however a small problem with VL multipliers: they break the
> Vectorisation Loop paradigm, turning it effectively into a SIMD-like
> one instead.
Not really, see following examples.
>
> i am slightly concerned that the templates for VL-based loops would
> need to be much more complex (less uniform), as the multipliers now
> need to be taken into account within the loop, on a per-instruction
> basis instead of a per-loop basis.
>
VL multipliers are basically embedding the short-length (1 to 4) SIMD
vectors used in Vulkan shaders into a VL-based vectorization loop.
With standard VL-based vectorization, the loop:
float a[], b[], c[], d[];
for(int i = 0; i < 1000; i++)
{
a[i] = b[i] + c[i] * d[i];
}
vectorization produces:
for(int i = 0;;)
{
VL = setvl(1000 - i);
vecVL v = loadVL(&b[i]) + loadVL(&c[i]) * loadVL(&d[i]);
storeVL(&a[i], v);
i += VL;
}
With vl-multipliers, we can similarly vectorize the loop:
struct VertexIn
{
vec3 position;
vec3 normal;
vec4 color; // rgba
};
struct VertexOut
{
vec4 position; // xyzw
vec4 color;
};
VertexIn vertexes_in[];
VertexOut vertexes_out[];
vec3 light_dir;
float ambient, diffuse;
for(int i = 0; i < 1000; i++)
{
// calculate vertex colors using
// lambert's cos model and fixed ambient brightness
vec3 n = vertexes_in[i].normal;
vec3 l = light_dir;
float dot = n.x * l.x + n.y * l.y + n.z * l.z;
float brightness = max(dot, 0.0) * diffuse + ambient;
vec4 c = vertexes_in[i].color;
c.rgb *= brightness;
vertexes_out[i].color = c;
// orthographic projection
vertexes_out[i].position = vec4(vertexes_in[i].position, 1.0);
}
vectorization produces:
for(int i = 0;;)
{
VL = setvl(1000 - i);
vec3xVL n = load3xVL_strided(&vertexes_in[i].normal, sizeof(VertexIn));
vec3 l = light_dir;
vecVL dot = n.x * l.x + n.y * l.y + n.z * l.z;
vecVL brightness = max(dot, 0.0) * diffuse + ambient;
vec4xVL c = load4xVL_strided(&vertexes_in[i].color, sizeof(VertexIn));
vec3xVL c_rgb = c.rgb;
c_rgb *= brightness;
c.rgb = c_rgb;
store4xVL_strided(&vertexes_out[i].color, c, sizeof(VertexOut));
vec4xVL p = 1.0;
p.xyz = load3xVL_strided(&vertexes_in[i].position, sizeof(VertexIn));
store4xVL_strided(&vertexes_out[i].position, p, sizeof(VertexOut));
i += VL;
}
The vl-multipliers that Vulkan needs are 1, 2, 3, and 4.
The vl-multipliers that OpenCL needs are 1, 2, 3, 4, 8, and 16, though we
can probably get away with just 1 to 4 and use multiple vectors for 8 and
16.
So, hopefully, the examples I gave help show how vl-multipliers are
probably the most straightforward way to vectorize graphics code with
variable length vectors.
Jacob
More information about the libre-riscv-dev
mailing list