2

I find myself extensively using inline asm, and very often want to be able to use a register or a literal in a given bit of asm, however I cannot see how to make this an option with the Microchip XC16 suite.

As far as I can tell, you need to manually encode the literal symbol #, and this is an incompatible with a prefix. What this means is that the following code does not compile:

asm("MOV %1, %0" : "=r" (res) : "i" (1));
Invalid operands specified ('mov 1,w0').

But the following does:

asm("MOV #%1, %0" : "=r" (res) : "i" (1));

Which is of course incompatible with registers:

asm("MOV #%1, %0" : "=r" (res) : "ri" (x));
Invalid operands specified ('mov #w0,w0').

So it seems to me that Microchip are not following GCC conventions, where I believe the literal symbol should have been embedded in the operand and this is making it particularly hard to work with.

I was wondering on the off-chance.. does anyone have any bright ideas on how to work around this problem?

For now I'm passing __builtin_constant_p as an additional parameter that I then .if on in the asm as follows, but to say it gets unwieldy fast would be an understatement.

asm(".if %[isk]  \n"
    "MOV #%1, %0 \n"
    ".elseif     \n"
    "MOV %1, %0  \n"
    ".endif      \n"
    : "=r" (res)
    : "ri" (x), [isk] "i" (__builtin_constant_p(x));

And I don't even believe that GCC guarantees that %1 will be a literal if isk is true, which means having to if-then-else block it all out C side... sigh.


To clarify MOV is just a sample instruction. These processors (dsPIC33Es) have zero overhead single and multi-instruction loops which require asm to exploit, syntax of which looks like the following:

/* this code demonstrates compilation failure if cnt is a constant
 * as there is no # prefix before the %[cnt] */
asm("REPEAT %[cnt]        \n"
    "  MOV [%0++], [%1++] \n"
    : "+r" (src), "+r" (dst), "=m" (*dst)
    : "m" (*src), [cnt] "ri" (cnt));

This memcpy loop takes cnt+1 cycles to execute, which due to pipelining is actually twice as fast as if you completely unrolled the loop and 6 times faster than branching each iteration. Along with their multi-instruction DO loop variant they're pretty essential to get the most out of these processors.

Mania
  • 1,718
  • 14
  • 16
  • 2
    What's the problem you are actually trying to solve? I'm sure you aren't trying to write a single `MOV` statement. Maybe there is a way to avoid the `MOV` altogether. – Florian Weimer Jul 25 '17 at 05:57
  • `asm("MOV %1, %0" : "=r" (res) : "i" (1));` should compile, and does in GCC targeting x86. [`i` is a valid constraint, meaning an immediate integer operand](https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html#Simple-Constraints). So this is evidently a bug in your compiler. Like Florian said, though, it is strange that you need to write `MOV` instructions in inline assembly, or really very much code *at all* in inline asm. Why is it that you find yourself "extensively using inline asm?" – Cody Gray - on strike Jul 25 '17 at 07:10
  • @CodyGray, x86 is different because immediate operands do not need prefixes. – Florian Weimer Jul 25 '17 at 07:28
  • Ah, I see what you mean now. It does look like a compiler bug/missing feature. – Florian Weimer Jul 25 '17 at 08:15
  • Sorry, to clarify, MOV is just a sample instruction - I've added a realistic example to the bottom of the post. And yes, with x86 target's support of the same syntax, it does seem to be a compiler bug/oversight :( – Mania Jul 25 '17 at 08:40
  • @Florian Yes, they do. GAS syntax uses a dollar-sign prefix on immediates. – Cody Gray - on strike Jul 25 '17 at 10:39
  • 1
    @CodyGray, I was think about `intel` syntax initially. But you are right, GCC synthesizes the `$` for `att`, as needed. If this target doesn't do it, sure looks like a compiler bug. Looks like Microchip doesn't offer sources of their compiler to the general public, so I couldn't take a quick peek at them to confirm. – Florian Weimer Jul 25 '17 at 10:48
  • I'll try reporting it to them, never know. I guess I was hoping that maybe there's a way of differentiating the constraint class in asm, or determine if a value is a literal.. or ideally a % prefix (such as %#0) that inserts the # in the case of a literal, but with trial and error I haven't had any such luck. Thank you for your help. – Mania Jul 25 '17 at 11:21
  • I have had a thought at how this came to be though - the asm considers non-# prefixed literals to be memory addresses, such that: `MOV 0x1000, w0` is valid (loading address 0x1000 to w0). It's just a nuisance in that the compiler does not seem to differentiate between literals and addresses, emitting them all as the same. – Mania Jul 25 '17 at 11:23
  • Have you tried some of the [alternatives](https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html#Simple-Constraints) to `"i"`? `"n"` looks promising. Maybe `"s"`. I don't think XC16 is part of the regular gcc source tree or I'd check out the code for TARGET_PRINT_OPERAND for the specific config. – David Wohlferd Jul 26 '17 at 05:08
  • @david-wohlferd Sadly, xc16 is most definitely closed source. I've attempted brute forcing it, trialling every letter from a-z and A-Z as both a constraint and as a modifier. No luck on literals but it wasn't entirely fruitless - undocumented `%m[param]` allows for constraint `"rU"` (register or near address) to work, which otherwise also suffered a syntax incompatibility. I have however found a dirty, dirty hack that kinda works - posting as a solution if you're interested. – Mania Jul 26 '17 at 07:47

1 Answers1

1

I have found a way to detect in asm if a given parameter is a literal or not, it's far from ideal but it seems to work.

First, in an asm header file, mark a symbol for each register:

.irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
  .set _IS_REG_w&r&, 1 ; mark all registers as "REGs"
  .set _IS_REG_W&r&, 1
.endr

And then to use:

.ifdecl _IS_REG_%0
  REPEAT %0
.else
  REPEAT #%0
.endif

Which can be wrapped up in an asm macro:

.macro REPEATN cnt
    .ifdecl _IS_REG_&cnt&
        REPEAT \cnt
    .else
        REPEAT #\cnt
    .endif
.endm

For easy embedding in inline asm:

void DelayCycles(int count)
{
    asm("REPEATN %[cnt]    \n"
        "    NOP           \n"
        :
        : [cnt] "ri" (count));
}
Mania
  • 1,718
  • 14
  • 16