Use GCC builtin intrinsic that requires immediate value as argument

Hi, firstly I wanted to thank those who started and participate on this website and forum as I was debating whether to ask this on Stackoverflow or here, but at least from briefly checking, this forum possibly seems to be a little more active than Ada-related topics on stack overflow.

This may be somewhat of rarer topic, and it likely possible that I may have to ask someone more closely related to GNAT and or GCC directly to remedy this problem, but I thought it was worth trying here first.

As some may already know, GNAT allows binding to or importing GCC ‘builtin’ functions that are lower level related features provided directly by the compiler itself (i.e. not an external library that is linked to). These builtin functions also include ‘intrinsic’ functions that provide language-level mappings to specific assembly instructions like SIMD ones.

Specifically, I have been trying to use x86 intrinsic __builtin_ia32_psrldqi128 which shifts the contents of a 128 bit XMM register to the right by the specified number of bytes. The problem is that the second argument, the number of bytes to shift by, must be an immediate value that is encoded within the instruction directly (i.e. not in a register), which when used in Ada looks like

with ada.text_io; use ada.text_io;                                                                                                                                                       
                                                                                                                                                                                         
with gnat.sse;                                                                                                                                                                         > 
with gnat.sse.vector_types;                                                                                                                                                              
                                                                                                                                                                                         
procedure test_program is                                                                                                                                                                
                                                                                                                                                                                         
   package sse_vt renames gnat.sse.vector_types;                                                                                                                                         
   package sse renames gnat.sse;                                                                                                                                                         
                                                                                                                                                                                         
   function ia32_psrldq (V : sse_vt.m128i; Imm : integer) return sse_vt.m128i                                                                                                            
     with import,                                                                                                                                                                        
          inline_always,                                                                                                                                                                 
          external_name => "__builtin_ia32_psrldqi128",                                                                                                                                  
          convention => intrinsic;                                                                                                                                                       
                                                                                                                                                                                         
   type vf32_view is array (positive range 1 .. 4) of sse.float32 with alignment => sse.vector_align;                                                                                    
                                                                                                                                                                                         
   v1 : sse_vt.m128i;                                                                                                                                                                    
   v1_view : vf32_view := (1.0, 1.0, 1.0, 1.0) with address => v1'address;                                                                                                               
                                                                                                                                                                                         
   v2 : sse_vt.m128i;                                                                                                                                                                    
   v2_view : vf32_view with address => v2'address;                                                                                                                                       
                                                                                                                                                                                         
begin                                                                                                                                                                                    
   v2:= ia32_psrldq (v1, 1);
   put_line (v2_view (1)'image & v2_view (2)'image & v2_view (3)'image & v2_view (4)'image);                                                                                                                                                             
end test_program;

C and C++ are able to use these as expected as long as the argument which is an immediate value is a constant, static expression known at compile time (i.e. not a variable), which would look like it does in the Ada example above at v2:= ia32_psrldq (v1, 1) where 1 is the immediate value, but when used in Ada it seems no matter how the argument is specified GCC interprets it as a non-immediate value (I say GCC specifically because the message appears to be emitted by a backend stage instead of GNAT itself).

The specific message it gives is (using GNAT 14.1.0 on macOS)

test_program.adb: In function 'test_program':
test_program.adb:26:9: error: the last argument must be an 8-bit immediate
gnatmake: "test_program.adb" compilation error

From I have found so far the best alternative (and likely only) option for now is to use inline assembly where constraints can be given that specifically convey to the compiler how a particular argument value should be interpreted. I have found other discussions about using other builtin intrinsic functions in Ada but none that specifically import ones that require an immediate value as argument (though it is possible I missed something).

I assume this is likely an oversight or bug within the Ada frontend of GCC due to how rarely intrinsics that require this have been used within Ada, but maybe there is something I missed, or if not, that maybe this can be fixed or added to GNAT at some point.

Thank you for reading and for any advice

This test program is build successfully by GCC 15 and GCC 16 on Linux.

It might be a bug in GCC 14 compiler.

Thank you for your reply and for trying the test @godunko, though I think it might need to be run a second time to be certain. I had already experienced this in another situation but didnt try checking the test program; that is if you had any level of optimization enabled it seems that since the variable the return value is written to is never used, GCC optimizes out the entire call to the intrinsic function (the instruction isn’t present when checking the temporary assembly file), I added a call to put_line to print the result that should hopefully prevent that from happening (it seems to on my system at least when using -O1).

I also tried using GCC 15.3.0 and the same situation occurs, and although unlikely I will try on Linux as well just incase.

Edit: The same problem happened with GCC 12.2.0 on Linux as well

You have

and the error message is

Perhaps making Imm an 8-bit type would help?