This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

ARM Vs GCC assembler

Note: This was originally posted on 25th January 2013 at http://forums.arm.com

Hi,

I have a question regarding the ARM and GCC assemblers i.e. ARMASM and GAS.

MOV.W R4,#0x87C0 is translated to F248_74C0 by ARMASM.

However, GAS throws up the error "invalid constant (87c0) after fixup" for the same instruction. Why does GAS give an error for this?

I realise the operand is 16-bit but I'm using the '.syntax unified'. I thought GAS supports .W and .N suffixes. Even without the .W suffix, GAS gives the same error whereas ARMASM quietly expands the 16-bit instruction to 32-bit.

I'm using 'GCC version 4.7.2 (Sourcety CodeBench Lite 2012.09-63).

Any insight as to why GAS is giving an error is appreciated. The workaround is to use two instructions. The following 2 instructions compile OK.

MOVW  R4  ,#0xC0
MOVT R4  ,#0x87

Thanks and regards,
Ger
  • Note: This was originally posted on 25th January 2013 at http://forums.arm.com

    What values are you using for the command line switches (-mcpu, -march, -mthumb, etc.)?
    What is your target processor/architecture?
  • Note: This was originally posted on 25th January 2013 at http://forums.arm.com

    For ARMv7-M,
      MOV.W R4, #87C0
    is incorrect but
      MOVW R4, #87C0
    is assembled by the gnu assembler to f248 74c0, using the T3 encoding of the MOV instruction.
  • Note: This was originally posted on 31st January 2013 at http://forums.arm.com

    Thanks for the reply Joe,

    I'm using the AMR cortex-m3 processor (ARM V7 architecture). MOVW R4,#0x87C0 works for both GCC and ARM assemblers.

    The GNU assembler command line switches  include: "-mcpu=cortex-m3" and "-mthumb"

    According to the GNU assembler documentation the .W and .N suffixes are supported but I guess terms and conditions apply!

    regards,
    Ger
  • Note: This was originally posted on 1st February 2013 at http://forums.arm.com

    I think what's going on here is confusion between the "W" in "MOVW" and ".W".

    ".W" means use a wide (32-bit) encoding for this instruction even if a 16-bit encoding exists.

    "MOVW" as a Thumb instruction is only available as a wide (.W) instruction, after all it needs room for a 16-bit immediate value; so "MOVW" and "MOVW.W" are the same thing.

    Apparently armasm is willing to translate "MOV rn,#imm" to "MOVW rn,#imm" if the immediate value requires it and the gas you are using is not willing.
  • Note: This was originally posted on 1st February 2013 at http://forums.arm.com

    Thanks for the reply Scott.

    Just one comment, the ARM V7 Thumb2 instruction set inlcudes a MOV.W encoding option (in addition to the MOVW encoding). ARMASM recognises MOV.W.

    According to the GNU assembler documentation provided by Sourcery CodeBenchLite 2012.09-63, use of the ".syntax unified" directive means that the .N and .W suffixes are recognised and honored.

    However, when using GAS assembler, the instruction "MOV.W R4,#0x87C0" gives "error: invalid constant (87c0) after fixup".  I've included the ".syntax unified" directive at the beginning of the source code.

    So the question is: does GAS support the .W suffix for MOV? It seems not but maybe I'm missing something?

    regards,
    Ger
  • Note: This was originally posted on 13th February 2013 at http://forums.arm.com

    All of your assertions look correct to me. Just a minor correction on a possible misunderstanding - the 32-bit Thumb-2 instructions are not like the 32-bit ARM instructions. It's a different sort of encoding with slightly different assembly level capabilities too, hence why the unified language is a subset of both.

    About the PC offset.. I don't know for sure, but something I do know is that even the big A-series processors with much longer superscalar pipelines still have the same PC semantics as the original 3-stage ARMs. Meaning that it's no longer a reflection of how the pipeline works but something that has logic to fake it in order to maintain compatibility.

    Cortex-M series processors don't have the traditional ARM instruction set so they don't have to worry about compatibility there, but they do have compatibility with the original 16-bit Thumb first introduced on ARM7s, and binary compatibility is a declared goal there. So regardless of what PC values make sense throughout the pipeline it has to appear to work the way it does to software. This may be moot since it wouldn't apply to Thumb-2 instructions, but if the PC really reflected the value two instructions ahead then it'd also vary depending on if a 32-bit or 16-bit instruction is next.
  • Note: This was originally posted on 15th February 2013 at http://forums.arm.com

    Thanks for the reply Exophase. It all adds to my understanding.
  • Note: This was originally posted on 4th February 2013 at http://forums.arm.com

    mov.w is the T2 encoding for the mov instruction. It's different from the movw instruction (which uses the T3 encoding, or A2 in ARM mode) in that it only has a 12-bit immediate instead of a 16-bit one. This format doesn't support the immediate you're specifying, hence the error. It'd probably work with a supported immediate.

    The ARM architecture reference manual says that just specifying mov should permit all encodings, while movw forces T3/A2. It doesn't seem to specify that the .w suffix is supposed to force the T2/A1 encodings, which is what GAS seems to be doing, or if it allows T3/A2 like ARMASM.
  • Note: This was originally posted on 4th February 2013 at http://forums.arm.com

    Thanks for the reply.

    Yes, I missed the fact that T2 specifies a 12-bit immediate value.

    I tried "MOV.W R4,0x87C" and I still get the 'invalid constant (0x87C) after fixup' error with GAS.

    Regards,
    Ger
  • Note: This was originally posted on 4th February 2013 at http://forums.arm.com

    0x87C isn't a valid 12-bit Thumb-2 immediate either. The encoding is more complex than just loading the bottom 12 bits. It's described in section A6.3.2 in the ARM v7a architecture reference manual (document DDI0406B). In a nutshell, it can be represented if the least to most significant bits span no more than 8 bit positions (your example spans 10) or if there are 8-bits repeated over 32-bits in a pattern of 0X0X, X0X0, or XXXX.

    If you need UAL code that assembles as both Thumb-2 and ARM the allowed immediates will be a little more limited.
  • Note: This was originally posted on 6th February 2013 at http://forums.arm.com

    Okay, I'll try to explain in more detail, although if you find the ARM manual cryptic you may find me worse..

    There's two variables at play here. The 12 bits are taken from the opcode and the 32 bits that are used as an operand when the instruction's executed. The processor expands those 12 bits into the 32-bit operand. The table shows how those 12 bits map to the 32 bits. The 12 bits are actually split into a few different fields scattered throughout the instruction. There's 8 bits at the bottom of the opcode, which the table labels a through h. Then there's another field of 3 bits in the middle which it calls imm3, and another single bit which it calls i. 5 bits are used as a control, so that when those 5 bits match some pattern bits a through h are expanded into the 32 bits as shown by the right-side column. You get the 5 control bits by concatenating i, imm3, and a together. An "x" means that either a 0 or 1 for the a bit will match this pattern.

    Are you familiar with how immediates work for the classic ARM ISA? If you are that helps a lot, because Thumb-2's is basically taking the same idea as a base but then improving it, at the expensive of needing more decoding logic in hardware.

    If you don't know, ARM's immediate mode is 12-bits and works like this: there's an 8-bit value and a 4-bit shift. The effective value of the immediate is then rotate_right(value, shift * 2). Because rotations wrap around you can use it to shift an 8-bit constant to the left. You can also use it to have immediates that have both the most significant and least significant bits set. This isn't that commonly useful, although I've used it a few times..

    Thumb-2 takes this basic format and does two things. First, it drops the ability to have immediates which cross over the edge and sticks with left shifts, meaning that there are only 24 useful shifts instead of 32. Then, it recognizes something similar to what floating point formats do - if you're left shifting a value you can assume that the most significant bit of what you're shifting is a 1 and you don't have to actually store it. With this you get 5 shift bits and 7 significant bits.

    Those 5 shift bits only need to store 24 types of shifts (since there's no more support for shifting over the edge), so the other 8 combinations are special. Since they don't involve shifts they need to use the full 8 bits for the immediate, giving another 2 bits leftover (down from 8 combinations to 4) to specify what to do with those 8 bits. These four options let you copy and paste the 8 bit immediate into the 32 bit operand in four different ways. Either it goes to bits 0:7, bits 0:7 and bits 16:23, bits 8:15 and bits 24:31, or bits 0:7, 8:15, 16:23, and 24:31.

    0x87C isn't valid because you can't describe it as shifting an 8-bit value to the left by some amount, nor can you describe it as copying an 8-bit value in one of the four ways given.
  • Note: This was originally posted on 11th February 2013 at http://forums.arm.com

    Thanks for the explanation, it helped a lot. I now understand that the first 4 encodings (i:imm3a: = 0000x,0001x, 0010x,0011x) are special encodings for various 8-bit repeat combinations. The remaining i:iim3:a  encodings are used for shift operations (assuming MSB=1, 5-bit shift + 7-bit immediate).

    Unfortunately, I'm not familiar with the classic ARM ISA. The ARM Cortex-M3 (ARMV7) is my first introduction to ARM processors - so I'm learning as I go!

    Another feature I'm getting to grips with is the thumb-2 instruction set concept. While I realise that the thumb-2 instruction set is a combination of 16-bit (thumb) and 32-bit (ARM) instructions, I'm still a little unsure as to how the architecture handles thumb-2 instructions. The unified assembly langauge instruction set means that the same syntax is used for both 16-bit and 32-bit instructions. Here is my understanding (basic level) - feel free to correct any errors.

    Q1) I'm assuming the assembler will try to use the 16-bit instruction encoding whenever possible. Depending on the registers used, immediate value size, if .W is specified etc, the assember will use a 32-bit instruction encoding.

    Q2) Are all instructions decoded as 32-bit by the processor logic? If the first 5-bits [15:11] of the instruction half-word are 0b11101, 0c11110 or 0b11111, the instruction is 32-bits. The 16-bit instructions are used to improve code density and are converted to 32-bit for execution.

    Q3) For the ARM V7 architecture with a 3-stage pipeline, the program counter value =  (address of current instruction being executed + 4). Does this still hold true even if an instruction is 32-bits? I assume it does but I'm unsure as to why it isn't = (address of current instruction + 6). 

    Regards,
    Ger