Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #32406 > unrolled thread

gcc arm inline asm: how to output value for .set directive?

Started byGrant Edwards <invalid@invalid.invalid>
First post2025-04-03 22:04 +0000
Last post2025-04-04 11:25 +0200
Articles 3 — 2 participants

Back to article view | Back to comp.arch.embedded


Contents

  gcc arm inline asm: how to output value for .set directive? Grant Edwards <invalid@invalid.invalid> - 2025-04-03 22:04 +0000
    Re: gcc arm inline asm: how to output value for .set directive? Grant Edwards <invalid@invalid.invalid> - 2025-04-03 23:06 +0000
      Re: gcc arm inline asm: how to output value for .set directive? David Brown <david.brown@hesbynett.no> - 2025-04-04 11:25 +0200

#32406 — gcc arm inline asm: how to output value for .set directive?

FromGrant Edwards <invalid@invalid.invalid>
Date2025-04-03 22:04 +0000
Subjectgcc arm inline asm: how to output value for .set directive?
Message-ID<vsn0lm$2k4$1@reader1.panix.com>
How do I convince ARM GCC's extended asm() to emit a value that can be
used in a .set directive?  Here's a simplified example:

    #include <stdint.h>
    
    typedef struct
    {
      uint8_t  what[32];
      uint32_t ever[2];
      unsigned foo;
      uint8_t bar[20];
    } shm_t;
    
    void foo(void)
    {
      asm("\t.global foo_offset");
      asm("\t.set foo_offset, 40");
      asm("\t.set foo_offset, %[off]" : : [off] "i" ( __builtin_offsetof(shm_t, foo) ) : );
    }

The first of the two .set directive works.  The second one doesn't.
The offsetof() value is being used as desired, but gcc is prefixing
the value with a '#' (presumably it thinks it's an instruction operand
and not a directive operand). Here's an excerpt from the assembly
listing:

  28              	@ 13 "foo.c" 1
  29              			.global foo_offset
  30              	@ 0 "" 2
  31              	@ 14 "foo.c" 1
  32              			.set foo_offset, 40
  33              	@ 0 "" 2
  34              	@ 15 "foo.c" 1
  35              			.set foo_offset, #40
  36              	@ 0 "" 2

And of course the assembler chucks a wobbly at line 35:

/tmp/cc1TaWVh.s: Assembler messages:
/tmp/cc1TaWVh.s:35: Error: bad expression
/tmp/cc1TaWVh.s:35: Error: junk at end of line, first unrecognized character is `4'

I've tried a bunch of gcc extended assembly input value constraints
and modifiers, but can't find the magic code that emits the value "40"
without the '#' on the front.

Any ideas?


[toc] | [next] | [standalone]


#32407

FromGrant Edwards <invalid@invalid.invalid>
Date2025-04-03 23:06 +0000
Message-ID<vsn49h$4eg$1@reader1.panix.com>
In reply to#32406
On 2025-04-03, Grant Edwards <invalid@invalid.invalid> wrote:
> How do I convince ARM GCC's extended asm() to emit a value that can be
> used in a .set directive?  Here's a simplified example:

>       asm("\t.set foo_offset, %[off]" : : [off] "i" ( __builtin_offsetof(shm_t, foo) ) : );

That didn't work, because gcc emits #40 instead of 40:

>   35              			.set foo_offset, #40

I finally stumbled across some example code that showed me the
answer. It's not the _constraint_ in the input operand list (the "i"
above) that matters (I had tried all upper/lower ascii letters).

You need a modifier in the _template_ string that references that
input operand:

  asm("\t.set foo_offset, %c[off]" : : [off] "i" ( __builtin_offsetof(shm_t, foo) ) : );
                           
The secret is the 'c' in "%c[off]"

Now that I know what to look for, I found it in the manual

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Generic-Operand-Modifiers

  6.11.2.8 Generic Operand Modifiers

I had completely missed the difference between a qualifier and a
modifier...

[toc] | [prev] | [next] | [standalone]


#32408

FromDavid Brown <david.brown@hesbynett.no>
Date2025-04-04 11:25 +0200
Message-ID<vso8hl$34cmn$1@dont-email.me>
In reply to#32407
On 04/04/2025 01:06, Grant Edwards wrote:
> On 2025-04-03, Grant Edwards <invalid@invalid.invalid> wrote:
>> How do I convince ARM GCC's extended asm() to emit a value that can be
>> used in a .set directive?  Here's a simplified example:
> 
>>        asm("\t.set foo_offset, %[off]" : : [off] "i" ( __builtin_offsetof(shm_t, foo) ) : );
> 
> That didn't work, because gcc emits #40 instead of 40:
> 
>>    35              			.set foo_offset, #40
> 
> I finally stumbled across some example code that showed me the
> answer. It's not the _constraint_ in the input operand list (the "i"
> above) that matters (I had tried all upper/lower ascii letters).
> 
> You need a modifier in the _template_ string that references that
> input operand:
> 
>    asm("\t.set foo_offset, %c[off]" : : [off] "i" ( __builtin_offsetof(shm_t, foo) ) : );
>                             
> The secret is the 'c' in "%c[off]"
> 
> Now that I know what to look for, I found it in the manual
> 
> https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Generic-Operand-Modifiers
> 
>    6.11.2.8 Generic Operand Modifiers
> 
> I had completely missed the difference between a qualifier and a
> modifier...
> 

Thank you for posting the answer after you figured it out!  I have 
probably read about gcc extended assembly more than most, and have no 
doubt also read about these modifiers, but I certainly did not remember 
them.  It's another little trick for the future.

(Note - you, Grant, are probably familiar with the ideas below, but 
other people might not be.)

I find that some of the inline assembly statements in my code are 
actually empty assembly and merely manipulate the dependencies and clobbers:


#define forceDependency(val) \
                 asm volatile("" :: "" (val) : )

This lets you force an evaluation within the volatile ordering of the 
code, as it tells the compiler that "val" is used in the assembly.  For 
example :

	volatile uint64_t * pv;
	uint64_t x = long_calculation...
	disable interrupts
	*pv = x;
	enable interrupts

Your aim here is to calculate the value, then do a 64-bit atomic store 
with minimal interrupt disabled time.  But the compiler can, and in some 
cases will, re-order the disable interrupt code with part or all of the 
long calculation code.  Putting a "forceDependency(x)" before the 
disable interrupt code blocks that.



#define forgetCompilerKnowledge(v) \
                 asm ("" : "+g" (v))

#define forgetCompilerBlock(start, size) \
     do { typedef struct { char x[size]; } XS; XS *p = (XS *) start; \
         asm ("" : "+m" (*p)); \
     } while (0);


This tells the compiler that "v" might be changed - the compiler has to 
forget any extra information it knows about it.  I've used this in 
connection with structures declared and defined in code, but modified 
post-link.  (I've also used it to work around a compiler bug.)


#define unspecifiedInt() \
     ({ int x; asm ("" : "=g" (x)); x; })

This creates an int out of thin air - an appropriate register is picked, 
and the int value is whatever was in the register before.  It is the 
absolute minimum code for when you want an object but don't care about 
the value - you just care that you are not invoking undefined behaviour 
or triggering a compiler warning or error message.


These kinds of tricks can also be very useful in benchmarking, testing, 
or isolating bits of code for examining the generated assembly.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.arch.embedded


csiph-web