为什么 MIPS 汇编程序将 subi 转换为 2 条指令?

Why does a MIPS assembler convert subi into 2 instructions?

我正在使用MARS (MIPS Assembly and Runtime Simulator) 4.5,它取代了伪指令

subi $sp, $sp, 4

包含两个指令的序列:

addi $at, $zero, 0x00000004
sub $sp, $sp, $at

为什么不把它当作

addi $sp, $sp, -4

哪个是单指令?

递减堆栈指针是一个非常常见的操作,很难相信汇编程序的作者没有注意到效率低下。

嗯,spim 没有 subi [你使用 sub 伪操作]。

mars 可以做得更好。也许应该提交错误报告?

但是,它的伪操作由一个文本文件控制:PseudoOps.txt 用于开发 match/translation table。因此,并非所有翻译 [即使是简单的翻译] 都是可能的。

来自该文件:

# Copyright (c) 2003-2010,  Pete Sanderson and Kenneth Vollmar
#
# Developed by Pete Sanderson (psanderson@otterbein.edu)
# and Kenneth Vollmar (kenvollmar@missouristate.edu)
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject
# to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# (MIT license, http://www.opensource.org/licenses/mit-license.html)

# File containing definitions of MIPS pseudo-ops

# File format:
#   Each line contains specification for one pseudo-op, including optional description.
#   First item is source statement syntax, specified in same "example" parser format used for regular instructions.
#   Source statement specification ends with a tab.  It is followed by a tab-separated list of basic instruction
#   templates to complete and substitute for the pseudo-op.
#   Format for specifying syntax of templates is different from specifying syntax of source statement:
#      (n=0,1,2,3,...) is token position in source statement (operator is token 0, parentheses are tokens but commas aren't)
#      RGn means substitute register found in n'th token of source statement
#      NRn means substitute next higher register than the one in n'th token of source code
#      OPn means substitute n'th token of source code as is
#      LLn means substitute low order 16-bits from label address in source token n.
#      LLnU means substitute low order 16-bits (unsigned) from label address in source token n.
#      LLnPm (m=1,2,3,4) means substitute low order 16-bits from label address in source token n, after adding m.
#      LHn means substitute high order 16-bits from label address in source token n. Must add 1 if address bit 15 is 1.
#      LHnPm (m=1,2,3,4) means substitute high order 16-bits from label address in source token n, after adding m. Must then add 1 if bit 15 is 1.
#      VLn means substitute low order 16-bits from 32-bit value in source token n.
#      VLnU means substitute low order 16-bits (unsigned) from 32-bit value in source token n.
#      VLnPm (m=1,2,3,4) means substitute low order 16-bits from 32-bit value in source token n, after adding m to value.
#      VLnPmU (m=1,2,3,4) means substitute low order 16-bits(unsigned) from 32-bit value in source token n, after adding m to value.
#      VHLn means substitute high order 16-bits from 32-bit value in source token n.  Use this if later combined with low order 16-bits using "ori ,,VLnU". See logical and branch operations.
#      VHn means substitute high order 16-bits from 32-bit value in source token n, then add 1 if value's bit 15 is 1.  Use this only if later instruction uses VLn() to calculate 32-bit address.  See loads and stores.
#      VHLnPm (m=1,2,3,4) means substitute high order 16-bits from 32-bit value in source token n, after adding m.  See VHLn.
#      VHnPm (m=1,2,3,4) means substitute high order 16-bits from 32-bit value in source token n, after adding m. Must then add 1 if bit 15 is 1. See VHn.
#      LLP is similar to LLn, but is needed for "label+100000" address offset. Immediate is added before taking low order 16.
#      LLPU is similar to LLn, but is needed for "label+100000" address offset. Immediate is added before taking low order 16 (unsigned).
#      LLPPm (m=1,2,3,4) is similar to LLP except m is added along with immediate before taking low order 16.
#      LHPA is similar to LHn, but is needed for "label+100000" address offset. Immediate is added before taking high order 16.
#      LHPN is similar to LHPA, used only by "la" instruction. Address resolved by "ori" so do not add 1 if bit 15 is 1.
#      LHPAPm (m=1,2,3,4) is similar to LHPA except value m is added along with immediate before taking high order 16.
#      LHL means substitute high order 16-bits from label address in token 2 of "la" (load address) source statement.
#      LAB means substitute textual label from last token of source statement.  Used for various branches.
#      S32 means substitute the result of subtracting the constant value in last token from 32.  Used by "ror", "rol".
#      DBNOP means Delayed Branching NOP - generate a "nop" instruction but only if delayed branching is enabled.  Added in 3.4.1 release.
#      BROFFnm means substitute n if delayed branching is NOT enabled otherwise substitute m.  n and m are single digit numbers indicating constant branch offset (in words).  Added in 3.4.1 release.
#      COMPACT is a marker to separate the default template from a second template optimized for 16-bit addresses.  See loads and stores having (data) label operands.
#   Everything else is copied as is into the generated statement (you must use register numbers not mnemonics)
#   The list of basic instruction templates is optionally followed a description of the instruction for help purposes.
#   To add optional description, append a tab then the '#' character followed immediately (no spaces) by the description.
#
#  See documentation for ExtendedInstruction.makeTemplateSubstitutions() for more details.
#
#  Matching for a given instruction mnemonic is first-fit not best-fit.  If an instruction has both 16 and 32-bit
#  immediate operand options, they should be listed in that order (16-bit version first).  Otherwise the 16-bit
#  version will never be matched since the 32-bit version fits small immediate values first.
#
#  The pseudo-op specification must start in the first column.  If first column is blank, the line will be skipped!
#
#  When specifying the example instruction (first item on line), the conventions I follow are:
#  - for a register operand, specify a numbered register (e.g. $t1 or $f1) to represent any register in the set.
#    The numerical value is not significant.  This is NOT the case when writing the templates that follow!
#    In the templates, numbered registers are parsed as is (use only [=10=] and , which are $zero and $at).
#  - for an immediate operand, specify a positive value indicative of the expected range.  I use 10 to represent
#    a 5 bit value, 100 to represent a 16-bit value, and 100000 to represent a 32-bit value.
#  - for a label operand, I use the string "label" (without the quotes).
#  The idea is to give the parser an example that will be parsed into the desired token sequence.  Syntax checking
#  is done by comparing the source token sequence to list of token sequences generated from the examples.
#  IMPORTANT NOTE:  The use of $t1,$t2, etc in the instruction sample means that any CPU register reference
#                   can be used in that position.  It is simply a placeholder.  By contrast, when
#                    is used in the template specification,  ($at) is literally placed into the generated
#                   instruction!  If you want the generated code to echo the source register, use RG1,RG2, etc.

#######################  arithmetic and branch pseudo-ops #####################

not $t1,$t2 nor RG1, RG2, [=10=]    #Bitwise NOT (bit inversion)

# Here are some "convenience" arithmetic pseduo-ops.  But do they encourage sloppy programming?
add $t1,$t2,-100    addi RG1, RG2, VL3  #ADDition : set $t1 to ($t2 plus 16-bit immediate)
add $t1,$t2,100000  lui , VHL3    ori , , VL3U    add RG1, RG2,     #ADDition : set $t1 to ($t2 plus 32-bit immediate)
addu $t1,$t2,100000 lui , VHL3    ori , , VL3U    addu RG1, RG2,    #ADDition Unsigned : set $t1 to ($t2 plus 32-bit immediate), no overflow
addi $t1,$t2,100000 lui , VHL3    ori , , VL3U    add RG1, RG2,     #ADDition Immediate : set $t1 to ($t2 plus 32-bit immediate)
addiu $t1,$t2,100000    lui , VHL3    ori , , VL3U    addu RG1, RG2,    #ADDition Immediate Unsigned: set $t1 to ($t2 plus 32-bit immediate), no overflow
sub $t1,$t2,-100        addi , [=10=], VL3    sub RG1, RG2,     #SUBtraction : set $t1 to ($t2 minus 16-bit immediate)
sub $t1,$t2,100000  lui , VHL3    ori , , VL3U    sub RG1, RG2,     #SUBtraction : set $t1 to ($t2 minus 32-bit immediate)
subu $t1,$t2,100000 lui , VHL3    ori , , VL3U    subu RG1, RG2,    #SUBtraction Unsigned : set $t1 to ($t2 minus 32-bit immediate), no overflow
subi $t1,$t2,-100       addi , [=10=], VL3    sub RG1, RG2,     #SUBtraction Immediate : set $t1 to ($t2 minus 16-bit immediate)
subi $t1,$t2,100000 lui , VHL3    ori , , VL3U    sub RG1, RG2,     #SUBtraction Immediate : set $t1 to ($t2 minus 32-bit immediate)
subiu $t1,$t2,100000    lui , VHL3    ori , , VL3U    subu RG1, RG2,    #SUBtraction Immediate Unsigned : set $t1 to ($t2 minus 32-bit immediate), no overflow
# feel free to add more convenience arithmetic pseduo-ops.

文件中没有更多与 add/sub 相关的条目。

旁注: 当 adding/subtracting 地址时,应该使用 unsigned 版本来防止溢出(可能用于特殊的$sp值)