5 ms·
I don't quite understand the objection here, this is fairly elegant in my opinion, you have a single opcode used to encode several operations by using "special
by simias 8y ago
I don't quite understand the objection here, this is fairly elegant in my opinion, you have a single opcode used to encode several operations by using "special cases". In my experience it's rather common in RISC IAs.
If the author doesn't like this they shouldn't look into MIPS because it goes well beyond that. You see, MIPS has a special "R0" register that's always 0 (AARCH 64 does as well by the way) so you can always use it as a placeholder in other instructions.
As such, there's no real MOVE instruction, it's just an assembler mnemonic that assembles down to `OR $target, $src, $R0`. NOP? It's by convention `SLL $R0, $R0, 0` (which has the nice property of being an instruction encoding as "0x00000000"). You want to negate a number? `SUB $target, $R0, $src".
Since all instructions are 32bit wide you can't load a 32bit immediate value in a single instruction, instead the assembler's "LI" mnemonic generates a pair of instructions (LUI/ORI) for large immediate values (ARM prefers PC-relative loads).
You have a whole bunch of mnemonics in MIPS that are just aliases around other instructions. I always thought it was pretty clever.
In summary you can have this assembler listing:
1:
sll $0, $0, 0
or $t0, $t1, $0
li $t0, 0xabcdef
sub $t0, $0, $t1
j 1b
That will disassemble to:
nop
move t0,t1
lui t0,0xab
ori t0,t0,0xcdef
j 0x0
neg t0,t1
The only operation here that I would qualify as "high level" is
the reordering of the "neg" instruction into the delay slot (note
that j is no longer the last instruction). Everything else is
very straightforward substitution and if the assembler didn't
support these mnemonics we could implement them with very trivial
macros.
Note that even x86 assemblers do that to some extent, for
instance "nop" assembles down to an instruction with no side
effect (typically `xchg eax, eax`). Furthermore there are a bunch
of mnemonics for the same encoding, for instance JAE (jump if
above or equal), JNB (jump if not below) and JNC (jump if not
carry). Overall instruction encoding is also massively more
complicated in x86 (and even more for amd64) so the assembler
needs to handle many more corner cases than the simple
substitutions of ARM and MIPS. As a brain teaser, consider the
following similar looking amd64 instructions that load the 32bit
value pointed at by a register (the only difference is that the
first one dereferences the pointer in %rax, the second in %r12):
mov %eax, (%rax) ; assembles to 89 00
mov %eax, (%r12) ; assembles to 41 89 04 24
I can't even be bothered to walk you through this but basically
it has to do with the fact that %r12 happens to be encoded as
%rsp + 8 (because registers r8 to r15 are effectively a hack
since x86 only supported 8 GPRs) and %rsp has special semantics
in this addressing mode which mandate a different, longer
encoding otherwise you end up with an ambiguous instruction.
Yeah, I think in retrospect we can give ARM a pass for their ROR
shenanigans.
- megaremote 8y ago> I don't quite understand the objection here, this is fairly elegant in my opinion, you have a single opcode used to encode several operations by using "special cases". Sounds like CISC.
- pjc50 8y agoBut why does anyone care where the boundary is drawn, especially based on entirely subjective opinions about whether one thing is more complex than another?
- simias 8y agoI don't understand how you reach this conclusion, if anything it's the opposite of CISC: instead of having highly specialized instructions you have a single generic instruction that can be used in all sorts of context. Note that this is very different from x86 "overloaded" encoding where a single mnemonic can have a million different encodings doing wildly different things depending on the operands, prefixes, lunar phase and/or operating mode[1], in this case there's no additional complexity to the "OR" implementation besides the adjunction of an R0 register (which is not specific to this opcode). You can still describe the encoding and functionality in a single line[2]. [1] https://svkt.org/~simias/up/20180926-112325_x86-mov.png https://svkt.org/~simias/up/20180926-112325_x86-mov.png [2] https://svkt.org/~simias/up/20180926-112224_or-encoding.png https://svkt.org/~simias/up/20180926-112224_or-encoding.png
- bonzini 8y agoNo, it's very different. CISC has different encodings for MOV and OR (and often subtle differences, e.g. OR updates the flags and MOV doesn't). On RISC processors, which have MOV as a special case of OR, the assembler accepts MOV at the source code level but the processor does not have to implement a separate MOV instruction at the binary level. Therefore the instruction set is indeed reduced. ARM's peculiarity is that the fundamental ALU operation is "R1 op (R2 shiftop R3)" or "R1 op (R2 shiftop #nn". But it's still not a CISC design, it's just that the barrel shifter is at a different place in the ALU and that shows in the instruction encoding. Apart from this quirk the ideas from the previous paragraph apply just as well to ARM.