検索条件
全1件
(1/1ページ)
This contents is translated from following web page.http://www.ethernut.de/en/documents/arm-inline-asm.html
It is almost translated by translate.google.co.jp. But I modify them to understand for me, maybe.
I hope you reading and writing assembler code with ARM. Let's enjoy!
The GNU C compiler for ARM RISC processors offers, to embed assembly language code into C programs.ARM RISCプロセッサー用のGNU Cコンパイラは、Cプログラムにアセンブリ言語コードを埋め込むことを提供します。
This cool feature may be used for manually optimizing time critical parts of the software or to use specific processor instruction, which are not available in the C language.
It's assumed, that you are familiar with writing ARM assembler programs, because this is not an ARM assembler programming tutorial.この記事は、ARMアセンブラプログラミングのチュートリアルではありませんので、
It's not a C language tutorial either.
All samples had been tested with GCC version 4, but most of them should work with earlier versions too.すべてのサンプルは、GCCバージョン4でテストされていますが、
Let's start with a simple example.シンプルな例から始めましょう。
The following statement may be included in your code like any other C statement.
/* NOP example */ asm("mov r0,r0");
It moves the contents of register r0 to register r0.これは、レジスタr0の内容を、レジスタr0へ移します。
In other words, it doesn't do much more than nothing.
It is also known as a NOP (no operation) statement and is typically used for very short delays.
Stop! Before adding this example right away to your C code, keep on reading and learn, why this may not work as expected.待ってください!
With inline assembly you can use the same assembler instruction mnemonics as you'd use for writing pure ARM assembly code.あなたは純粋なARMアセンブリコードを書くために使用したいようインラインアセンブリを使用すると、
And you can write more than one assembler instruction in a single inline asm statement.
To make it more readable, you can put each instruction on a separate line.
asm( "mov r0, r0\n\t" "mov r0, r0\n\t" "mov r0, r0\n\t" "mov r0, r0" );
The special sequence of linefeed and tab characters will keep the assembler listing looking nice.改行やタブ文字の特別なシーケンスは、見栄えの良いアセンブラリストを維持します。
It may seem a bit odd for the first time, but that's the way the compiler creates its own assembler code while compiling C statements.
So far, the assembler instructions are much the same as they'd appear in pure assembly language programs.これまでのところ、アセンブラ命令は、彼らは純粋なアセンブリ言語プログラムで
However, registers and constants are specified in a different way, if they refer to C expressions.
The general form of an inline assembler statement is
asm(code : output operand list : input operand list : clobber list);
The connection between assembly language and C operands is provided by an optional second and third part of the asm statement, the list of output and input operands.アセンブリ言語とCオペランドとの間の接続は、asm文と、オプションの第二の部分(出力オペランドリスト)と
We will explain the third optional part, the list of clobbers, later.
The next example of rotating bits passes C variables to assembly language.ビット回転の次の例では、アセンブリ言語にC変数を渡します。
It takes the value of one integer variable, right rotates the bits by one and stores the result in a second integer variable.
/* Rotating bits example */ asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));
Each asm statement is divided by colons into up to four parts:各asm文は、4つの部分までに、コロンで分割されます。
1.The assembler instructions, defined in a single string literal:アセンブラ命令、1つの文字列リテラルで定義される
"mov %[result], %[value], ror #1"
An optional list of output operands, separated by commas.コンマで区切られた、オプションの出力オペランドリスト。
Each entry consists of a symbolic name enclosed in square brackets, followed by a constraint string, followed by a C expression enclosed in parentheses.
Our example uses just one entry:
[result] "=r" (y)
A comma separated list of input operands, which uses the same syntax as the list of output operands.コンマで区切られた、出力オペランドリストと同じような文法を使う、入力オペランドリスト。
Again, this is optional and our example uses one operand only:
[value] "r" (x)
Optional list of clobbered registers, omitted in our example.オプションのclobberedレジスタリストは、この例では省略されています。
As shown in the initial NOP example, trailing parts of the asm statement may be omitted, if unused.最初のNOPの例に示すように、未使用の場合は、asm文の末尾部分は、省略してもよいです。
Inline asm statements containing assembler instruction only are also known as basic inline assembly, while statements containing optional parts are called extended inline assembly.
If an unused part is followed by one which is used, it must be left empty.
The following example sets the current program status register of the ARM CPU.
It uses an input, but no output operand.
asm("msr cpsr,%[ps]" : : [ps]"r"(status));
Even the code part may be left empty, though an empty string is required.コード部分が空のままであっても、からの文字列を渡すことが要求されます。
The next statement creates a special clobber to tell the compiler, that memory contents may have changed.
Again, the clobber list will be explained later, when we take a look to code optimization.
asm("":::"memory");
You can insert spaces, newlines and even C comments to increase readability.あなたは、読みやすさを向上させるために、スペース、改行、さらにはCのコメントを挿入することができます。
In the code section, operands are referenced by a percent sign followed by the related symbolic name enclosed in square brackets.コードセクションでは、オペランドは、角括弧で囲まれた関連シンボリック名が続くパーセント記号によって参照されます。
It refers to the entry in one of the operand lists that contains the same symbolic name.
From the rotating bits example:
%[result] refers to output operand, the C variable y, and
%[value] refers to the input operand, the C variable x.
%[result] | 出力オペランドを参照します。Cの変数はyです。 |
%[value] | 入力オペランドを参照します。Cの変数はxです。 |
Symbolic operand names use a separate name space.シンボリックオペランド名が別々の名前空間を使用します。
That means, that there is no relation to any other symbol table.
To put it simple: You can choose a name without taking care whether the same name already exists in your C code.
However, unique symbols must be used within each asm statement.
If you already looked to some working inline assembler statements written by other authors, you may have noticed a significant difference.既に他の著者によって書かれた、動いているインラインアセンブラ文を見た場合に、
In fact, the GCC compiler supports symbolic names since version 3.1. For earlier releases the rotating bit example must be written as
asm("mov %0, %1, ror #1" : "=r" (result) : "r" (value));
Operands are referenced by a percent sign followed by a single digit, where %0 refers to the first %1 to the second operand and so forth.オペランドは、単一の数字が続くパーセント記号によって参照されます。%0は1つ目の、%1は2つ目のオペランド、のように。
This format is still supported by the latest GCC releases, but quite error-prone and difficult to maintain.
Imagine, that you have written a large number of assembler instructions, where operands have to be renumbered manually after inserting a new output operand.
If all this stuff still looks a little odd, don't worry.すべてのこのようなものが、まだ少し奇妙に見える場合は、心配しないでください。
Beside the mysterious clobber list, you have the strong feeling that something else is missing, right? Indeed, we didn't talk about the constraint strings in the operand lists.
I'd like to ask for your patience.
There's something more important to highlight in the next chapter.
There are two possible reasons why you want to use assembly language.あなたはアセンブリ言語を使用する2つの理由があります。
First is, that C is limited when we are getting closer to the hardware.
E.g. there's no C statement for directly modifying the processor status register.
The second reason is to create highly optimized code.
No doubt, the GNU C code optimizer does a good job, but the results are far away from handcrafted assembler code.
The subject of the chapter is often overlooked: When adding assembly language code by using inline assembler statements, this code is also processed by the C compiler's code optimizer.閑話休題。
Let's examine the part of a compiler listing which may have been generated from our rotating bits example:
00309DE5 ldr r3, [sp, #0] @ x, x E330A0E1 mov r3, r3, ror #1 @ tmp, x 04308DE5 str r3, [sp, #4] @ tmp, y
The compiler selected register r3 for bit rotation.コンパイラは、レジスタr3をビットローテーションのために選択しました。
It could have selected any other register or two registers, one for each C variable.
It may not explicitly load the value or store the result.
Here is another listing, generated by a different compiler version with different compile options:
E420A0E1 mov r2, r4, ror #1 @ y, x
The compiler selected a unique register for each operand, using the value already cached in r4 and passing the result to the following code in r2.コンパイラは、それぞれのオペランドにユニークなレジスタを選択しました。
Did you get the picture?
Often it becomes worse.多くの場合、悪くなります。
The compiler may even decide not to include your assembler code at all.
These decisions are part of the compiler's optimization strategy and depend on the context in which your assembler instructions are used.
For example, if you never use any of the output operands in the remaining part of the C program, the optimizer will most likely remove your inline assembler statement.
The NOP example we presented initially may be such a candidate as well, because to the compiler this is useless overhead, slowing down program execution.
The solution is to add the volatile attribute to the asm statement to instruct the compiler to exclude your assembler code from code optimization.解決策は、コード最適化から、あなたのアセンブラコードを除外しないよう、
Remember, that you have been warned to use the initial example.
Here is the revised version:
/* NOP example, revised */ asm volatile("mov r0, r0");
But there is more trouble waiting for us.しかし、私たちを待っている多くの問題があります。
A sophisticated optimizer will re-arrange the code.
The following C snippet had been left over after several last minute changes:
i++; if (j == 1) x += 3; i++;
The optimizer will recognize, that the two increments do not have any impact on the conditional statement.オプティマイザは次のように認識するでしょう。2つのインクリメントは、条件文に影響を及ぼさないだろう。
Furthermore it knows, that incrementing a value by 2 will cost one ARM instruction only.
Thus, it will re-arrange the code to
if (j == 1) x += 3; i += 2;
and save one ARM instruction.そして、ARM命令を1つ節約します。
As a result: There is no guarantee, that the compiled code will retain the sequence of statements given in the source code.
This may have a great impact on your code, as we will demonstrate now.これは、以下にデモで示すように、貴方のコードに於いて、とても大きな影響があるでしょう。
The following code intends to multiply c with b, of which one or both may be modified by an interrupt routine.
Disabling interrupts before accessing the variables and re-enable them afterwards looks like a good idea.
asm volatile("mrs r12, cpsr\n\t" "orr r12, r12, #0xC0\n\t" "msr cpsr_c, r12\n\t" ::: "r12", "cc"); c *= b; /* This may fail. */ asm volatile("mrs r12, cpsr\n" "bic r12, r12, #0xC0\n" "msr cpsr_c, r12" ::: "r12", "cc");
Unfortunately the optimizer may decide to do the multiplication first and then execute both inline assembler instructions or vice versa.不幸なことに、オプティマイザは、乗算を先に行い、その後、インラインアセンブラ命令を実行することを決定します。もしくは逆。
This will make our assembly code useless.
We can solve this with the help of the clobber list, which will be explained now.ここで解説するクロバーリストの助けにより、これを解決できます。
The clobber list from the example above
"r12", "cc"
informs the compiler that the assembly code modifies register r12 and updates the condition code flags.アセンブラコードが、レジスタr12とcondition code flagsを更新することをコンパイラに情報を与えます。
Btw. using a hard coded register will typically prevent best optimization results.
In general you should pass a variable and let the compiler choose the adequate register.
Beside register names and cc for the condition register, memory is a valid keyword too.
It tells the compiler that the assembler instruction may change memory locations.
This forces the compiler to store all cached values before and reload them after executing the assembler instructions.
And it must retain the sequence, because the contents of all variables is unpredictable after executing an asm statement with a memory clobber.
asm volatile("mrs r12, cpsr\n\t" "orr r12, r12, #0xC0\n\t" "msr cpsr_c, r12\n\t" :: : "r12", "cc", "memory"); c *= b; /* This is safe. */ asm volatile("mrs r12, cpsr\n" "bic r12, r12, #0xC0\n" "msr cpsr_c, r12" ::: "r12", "cc", "memory");
Invalidating all cached values may be suboptimal.すべてのキャッシュされた値を無効化することは次善の策なのかもしれません。
Alternatively you can add a dummy operand to create an artificial dependency:
asm volatile("mrs r12, cpsr\n\t" "orr r12, r12, #0xC0\n\t" "msr cpsr_c, r12\n\t" : "=X" (b) :: "r12", "cc"); c *= b; /* This is safe. */ asm volatile("mrs r12, cpsr\n" "bic r12, r12, #0xC0\n" "msr cpsr_c, r12" :: "X" (c) : "r12", "cc");
This code pretends to modify variable b in the first asm statement and to use the contents variable c in the second.このコードは、最初のasmステートメントに変数bを変更すると第二のコンテンツを変数cを使用するフリをします。
This will preserve the sequence of our three statements without invalidating other cached variables.
It is essential to understand how the optimizer affects inline assembler statements.これは、オプティマイザは、インラインアセンブラ文にどのように影響するかを理解することが不可欠です。
If something remains nebulous, better re-read this part before moving on the the next topic.
We learned, that each input and output operand is described by a symbolic name enclosed in square bracket,私たちは、それぞれの入力および出力オペランドが、順番に括弧内のCの式が続く制約文字列が続く、
followed by a constraint string, which in turn is followed by a C expression in parentheses.
What are these constraints and why do we need them?どのようなこれらの制約があり、我々にはなぜそれが必要なのですか?
You probably know that every assembly instruction accepts specific operand types only.
For example, the branch instruction expects a target address to jump at.
However, not every memory address is valid, because the final opcode accepts a 24-bit offset only.
In contrary, the branch and exchange instruction expects a register that contains a 32-bit target address.
In both cases the operand passed from C to the inline assembler may be the same C function pointer.
Thus, when passing constants, pointers or variables to inline assembly statements, the inline assembler must know, how they should be represented in the assembly code.
For ARM processors, GCC 4 provides the following constraints.ARMプロセッサ用のGCC4は、以下の制約を提供します:
Constraint | Usage in ARM state | Usage in Thumb state |
---|---|---|
f | Floating point registers f0 .. f7 | Not available |
h | Not available | Registers r8..r15 |
G | Immediate floating point constant | Not available |
H | Same a G, but negated | Not available |
I | Immediate value in data processing instructions e.g. ORR R0, R0, #operand | Constant in the range 0 .. 255 e.g. SWI operand |
J | Indexing constants -4095 .. 4095 e.g. LDR R1, [PC, #operand] | Constant in the range -255 .. -1 e.g. SUB R0, R0, #operand |
K | Same as I, but inverted | Same as I, but shifted |
L | Same as I, but negated | Constant in the range -7 .. 7 e.g. SUB R0, R1, #operand |
l | Same as r | Registers r0..r7 e.g. PUSH operand |
M | Constant in the range of 0 .. 32 or a power of 2 e.g. MOV R2, R1, ROR #operand | Constant that is a multiple of 4 in the range of 0 .. 1020 e.g. ADD R0, SP, #operand |
m | Any valid memory address | |
N | Not available | Constant in the range of 0 .. 31 e.g. LSL R0, R1, #operand |
O | Not available | Constant that is a multiple of 4 in the range of -508 .. 508 e.g. ADD SP, #operand |
r | General register r0 .. r15 e.g. SUB operand1, operand2, operand3 | Not available |
w | Vector floating point registers s0 .. s31 | Not available |
X | Any operand |
Constraint characters may be prepended by a single constraint modifier.制約文字は単一の制約修飾子で、前に付加することができます。
Constraints without a modifier specify read-only operands.
Modifiers are:
Modifier | Specifies |
---|---|
= | Write-only operand, usually used for all output operands |
+ | Read-write operand, must be listed as an output operand |
& | A register that should be used for output only |
Output operands must be write-only and the C expression result must be an lvalue, which means that the operands must be valid on the left side of assignments.出力オペランドは書き込み専用にし、Cの式の結果は左辺値でなければなりません。
The C compiler is able to check this.
Input operands are, you guessed it, read-only.入力オペランドは、想像どおり、読込専用です。
Note, that the C compiler will not be able to check, whether the operands are of reasonable type for the kind of operation used in the assembler instructions.
Most problems will be detected during the late assembly stage, which is well known for its weird error messages.
Even if it claims to have found an internal compiler problem that should be immediately reported to the authors, you better check your inline assembler code first.
A strict rule is: Never ever write to an input operand.厳格なルールは次のとおりです:"これまでに入力オペランドに書き込むことはありません"。
But what, if you need the same operand for input and output?
The constraint modifier + does the trick as shown in the next example:
asm("mov %[value], %[value], ror #1" : [value] "+r" (y));
This is similar to our rotating bits example presented above.前述のローテートビットの例に酷似しています。
It rotates the contents of the variable value to the right by one bit.
In opposite to the previous example, the result is not stored in another variable.
Instead the original contents of input variable will be modified.
The modifier + may not be supported by earlier releases of the compiler.修飾子"+"は、コンパイラの以前のリリースではサポートされない場合があります。
Luckily they offer another solution, which still works with the latest compiler version.
For input operators it is possible to use a single digit in the constraint string.
Using digit n tells the compiler to use the same register as for the n-th operand, starting with zero.
Here is an example:
asm("mov %0, %0, ror #1" : "=r" (value) : "0" (value));
Constraint "0" tells the compiler, to use the same input register that is used for the first output operand.制約"0"は、コンパイラに、最初の出力オペランドで使われたレジスタと同じ入力レジスタを使うことを伝えます。
Note however, that this doesn't automatically imply the reverse case.これは、自動的に逆の場合を意味するものではありませんので、注意してください。
The compiler may choose the same registers for input and output, even if not told to do so.
You may remember the first assembly listing of the rotating bits example with two variables, where the compiler used the same register r3 for both variables.
The asm statement
asm("mov %[result],%[value],ror #1":[result] "=r" (y):[value] "r" (x));generated this code:
00309DE5 ldr r3, [sp, #0] @ x, x E330A0E1 mov r3, r3, ror #1 @ tmp, x 04308DE5 str r3, [sp, #4] @ tmp, y
This is not a problem in most cases, but may be fatal if the output operator is modified by the assembler code before the input operator is used.これは、ほとんどの場合、問題ではありませんが、入力演算子が使用される前に出力オペレータは、
In situations where your code depends on different registers used for input and output operands, you must add the & constraint modifier to your output operand.
The following code demonstrates this problem.
asm volatile("ldr %0, [%1]" "\n\t" "str %2, [%1, #4]" "\n\t" : "=&r" (rdv) : "r" (&table), "r" (wdv) : "memory");
A value is read from a table and then another value is written to another location in this table.値は、テーブルから読み取られ、次に別の値は、このテーブル内の別の場所に書き込まれます。
If the compiler would have chosen the same register for input and output, then the output value would have been destroyed on the first assembler instruction.
Fortunately, the & modifier instructs the compiler not to select any register for the output value, which is used for any of the input operands.
In order to reuse your assembler language parts, it is useful to define them as macros and put them into include files.アセンブラ言語の部分を再利用するためには、マクロとして定義し、includeファイルにそれらを置くことが有用である。
Using such include files may produce compiler warnings, if they are used in modules, which are compiled in strict ANSI mode.
To avoid that, you can write __asm__ instead of asm and __volatile__ instead of volatile.
These are equivalent aliases.
Here is a macro which will convert a long value from little endian to big endian or vice versa:ここでは、リトルエンディアンからビッグエンディアンに、またはその逆にlong値を変換するマクロをしめします:
#define BYTESWAP(val) \ __asm__ __volatile__ ( \ "eor r3, %1, %1, ror #16\n\t" \ "bic r3, r3, #0x00FF0000\n\t" \ "mov %0, %1, ror #8\n\t" \ "eor %0, %0, r3, lsr #8" \ : "=r" (val) \ : "0"(val) \ : "r3", "cc" \ );
Macro definitions will include the same assembler code whenever they are referenced.マクロ定義は、それらが参照されるたびに同じアセンブラコードが含まれます。
This may not be acceptable for larger routines.
In this case you may define a C stub function.
Here is the byte swap procedure again, this time implemented as a C function.ここでは、バイトスワップ手順を再び示します:この時、Cの関数として実装します。
unsigned long ByteSwap(unsigned long val) { asm volatile ( "eor r3, %1, %1, ror #16\n\t" "bic r3, r3, #0x00FF0000\n\t" "mov %0, %1, ror #8\n\t" "eor %0, %0, r3, lsr #8" : "=r" (val) : "0"(val) : "r3" ); return val; }
By default GCC uses the same symbolic names of functions or variables in C and assembler code.デフォルトでは、GCCは、Cとアセンブラコード内の関数や変数の同じシンボル名を使用しています。
You can specify a different name for the assembler code by using a special form of the asm statement:
unsigned long value asm("clock") = 3686400;
This statement instructs the compiler to use the symbolic name clock rather than value.この文は、値ではなく、シンボリック名の"clock"を使用するようにコンパイラに指示します。
This makes sense only for global variables.
Local variables (aka auto variables) do not have symbolic names in assembler code.
Replacing symbolic names of C functionsC関数のシンボリック名を置き換えます
In order to change the name of a function, you need a prototype declaration, because the compiler will not accept the asm keyword in the function definition:
extern long Calc(void) asm ("CALCULATE");
Calling the function Calc() will create assembler instructions to call the function CALCULATE.関数Calc()を呼び出す際には、関数CALCULATEを呼び出すためのアセンブラ命令を作成します。
A local variable may be held in a register.ローカル変数は、レジスタに保持されることがあります。
You can instruct the inline assembler to use a specific register for it.
void Count(void) { register unsigned char counter asm("r3"); ... some code... asm volatile("eor r3, r3, r3" : "=l" (counter)); ... more code... }
The assembler instruction, "eor r3, r3, r3", will clear the variable counter.アセンブラ命令、"eor r3, r3, r3"は、変数カウンタをクリアします。
Be warned, that this sample is bad in most situations, because it interferes with the compiler's optimizer.
Furthermore, GCC will not completely reserve the specified register.
If the optimizer recognizes that the variable will not be referenced any longer, the register may be re-used.
But the compiler is not able to check whether this register usage conflicts with any predefined register.
If you reserve too many registers in this way, the compiler may even run out of registers during code generation.
If you are using registers, which had not been passed as operands, you need to inform the compiler about this.あなたがオペランドとして渡されていないレジスタを使用している場合は、このことについてコンパイラに通知する必要があります。
The following code will adjust a value to a multiple of four.
It uses r3 as a scratch register and lets the compiler know about this by specifying r3 in the clobber list.
Furthermore the CPU status flags are modified by the ands instruction and thus cc had been added to the clobbers.
asm volatile( "ands r3, %1, #3" "\n\t" "eor %0, %0, r3" "\n\t" "addne %0, #4" : "=r" (len) : "0" (len) : "cc", "r3" );
Again, hard coding register usage is always bad coding style.再度書きますが、ハードレジスタの使用をコーディングすることは、常に悪いコーディングスタイルです。
Better implement a C stub function and use a local variable for temporary values.
You can use the mov instruction to load an immediate constant value into a register.あなたはレジスタに即値定数値をロードするmov命令を使用することができます。
Basically, this is limited to values ranging from 0 to 255.
asm("mov r0, %[flag]" : : [flag] "I" (0x80));
But also larger values can be used when rotating the given range by an even number of bits.偶数のビット数だけ回転させた範囲で、より大きな値を使用することができます。
In other words, any result of
n * 2^x
with n is in the mentioned range of 0 to 255 and x is an even number in the range of 0 to 24.
Because of rotation, x may be set to 26, 28 or 30, in which case bits 37 to 32 are folded to bits 5 to 0 resp.
Last not least, the binary complement of these values may be given, when using mvn instead of mov.
Sometimes you need to jump to a fixed memory address, which may be defined by a preprocessor macro.時々、プリプロセッサマクロに依って定義された固定メモリアドレスへジャンプしたい時があります。
You can use the following assembly code:
ldr r3, =JMPADDR bx r3
This will work with any legal address value.これは任意の有効なアドレス値で動作します。
If the constant fits (for example 0x20000000), then the smart assembler will convert this to
mov r3, #0x20000000 bx r3
If it doesn't fit (for example 0x00F000F0), then the assembler will load the value from the literal pool.もしフィットしないなら(例えば、0x00F000F0)、アセンブラはリテラルプールから値を読みだすでしょう:
ldr r3, .L1 bx r3 ... .L1: .word 0x00F000F0
With inline assembly it works in the same way.インラインアセンブリでも、同様にふるまいます。
But instead of using ldr, you can simply provide a constant as a register value:
asm volatile("bx %0" : : "r" (JMPADDR));
Depending on the actual value of the constant, either mov, ldr or any of its variants is used.定数の実際の値に依存して、ldrまたは、その変形が使われます。
If JMPADDR is defined as 0xFFFFFF00, then the resulting code will be similar to
mvn r3, #0xFF bx r3
The real world is more complicated.現実では、もっと複雑です。指定されたレジスタに定数をロードする必要が起きるでしょう。
It may happen, that we need to load a specific register with a constant.
Let's assume, that we want to call a subroutine, but we want to return to another address than the one that follows our branch.
This is can be useful when embedded firmware returns from main.
In this case we need to load the link register. Here is the assembly code:
ldr lr, =JMPADDR ldr r3, main bx r3
Any idea how to implement this in inline assembly? Here is a solution:インラインアセンブリで実装するアイデアはあるでしょうか。
asm volatile( "mov lr, %1\n\t" "bx %0\n\t" : : "r" (main), "I" (JMPADDR));
But there is still a problem.しかし、少し問題があります。
We use mov here and this will work as long as the value of JMPADDR fits.
The resulting code will be the same than what we get in pure assembly code.
If it doesn't fit, then we need ldr instead.
But unfortunately there is no way to express in inline assembly.
ldr lr, =JMPADDR
Instead, we must write代わりに、以下のように書かなければなりません。
asm volatile( "mov lr, %1\n\t" "bx %0\n\t" : : "r" (main), "r" (JMPADDR));
Compared to the pure assembly code, we end up with an additional statement, using an additional register.ピュアアセンブリコードと比べましょう。
ldr r3, .L1 ldr r2, .L2 mov lr, r2 bx r3
It is always a good idea to analyze the assembly listing output of the C compiler and study the generated code.常にCコンパイラのアセンブリ出力リストを分析し、生成されたコードを勉強することをお勧めします。
The following table of the compiler's typical register usage will be probably helpful to understand the code.
Register | Alt.Name | Usage |
---|---|---|
r0 | a1 | First function argument Integer function result Scratch register |
r1 | a2 | Second function argument Scratch register |
r2 | a3 | Third function argument Scratch register |
r3 | a4 | Fourth function argument Scratch register |
r4 | v1 | Register variable |
r5 | v2 | Register variable |
r6 | v3 | Register variable |
r7 | v4 | Register variable |
r8 | v5 | Register variable |
r9 | v6 | Register variable |
rfp | Real frame pointer | |
r10 | sl | Stack limit |
r11 | fp | Argument pointer |
r12 | ip | Temporary workspace |
r13 | sp | Stack pointer |
r14 | lr | Link register Workspace |
r15 | pc | Program counter |
Developers often expect, that a sequence of instructions remains in the final code as specified in the source code.開発者は、多くの場合、ソースコードで指定された命令のシーケンスは、最終的なコードに残っていることを、期待しています。
This assumption is wrong and often introduces hard to find bugs.
Actually, asm statements are processed by the optimizer in the same way as other C statements.
They may be rearranged if dependencies allow this.
The chapter "C code optimization" discusses the details and offers solutions."C code optimization"の章で、詳細を議論し、解決法を提案しています。
Even if a variable had been forcibly assigned to a specific register, the resulting code may not work as expected.変数を強制的に特定のレジスタに割り当てられていた場合であっても、予想通り、結果のコードは動作しない可能性があります。
Consider the following snippet:
int foo(int n1, int n2) { register int n3 asm("r7") = n2; asm("mov r7, #4"); return n3; }
The compiler is instructed to use r7 as a local variable n3, which is initialized by parameter n2.コンパイラは、パラメータn2で初期化されているローカル変数n3としてr7を使用するように指示されます。
Then the inlined assembly statement sets r7 to 4, which should be finally returned.
However, this may go completely wrong.
Remember, that compiler cannot recognize, what's happening inside the inline assembly.
But the optimizer is smart on the C code, generating the following assembly code.
foo: mov r7, #4 mov r0, r1 bx lr
Instead of returning r7, the value of n2 is returned, which had been passed to our function in r1.r7を返す代わりに、私たちの関数に渡されていたr1に入っているn2の値が返されます。
What happed here? Well, while the final code still contains our inline assembly statement, the C code optimizer decided, that n3 is not required.
It directly returns parameter n2 instead.
Just assigning a variable to a fixed register does not mean, that the C compiler will use that variable.固定レジスタに変数を割り当てるとが、Cコンパイラはその変数を使用すること、を直ちに意味するものではありません。
We still have to tell the compiler, that a variable is modified inside the inline assembly operation.
For the given example, we need to extend the asm statement with an output operator:
asm("mov %0, #4" : "=l" (n3));
Now the C compiler is aware, that n3 is modified and will generate the expected result:今、Cコンパイラが認識して、n3が変更されると予想される結果を生成します:
foo: push {r7, lr} mov r7, #4 mov r0, r7 pop {r7, pc}
Be aware, that, depending on the given compile options, the compiler may switch to thumb state.指定されたコンパイルオプションに応じて、コンパイラがThumb状態に切り替えることができる、ということに注意してください。
Using inline assembler with instructions that are not available in thumb state will result in cryptic compile errors.
In most cases the compiler will correctly determine the size of the assembler instruction, but it may become confused by assembler macros.ほとんどの場合、コンパイラは正しくアセンブラ命令のサイズを決定しますが、それはアセンブラマクロによって混乱することがあります。
Better avoid them.
In case you are confused: This is about assembly language macros, not C preprocessor macros.あなたが混乱しているケース:これは、アセンブリ言語のマクロではなく、Cプリプロセッサマクロについてです。
It is fine to use the latter.
Within the assembler instruction you can use labels as jump targets.アセンブラ命令内では、ジャンプターゲットとしてラベルを使用することができます。
However, you must not jump from one assembler instruction into another.
The optimizer knows nothing about those branches and may generate bad code.
Inline assembly instruction cannot contain preprocessor macros, because for the preprocessor these instruction are nothing else but string constants.プリプロセッサのために、これらの命令は何もないが、文字列定数であるため、
If your assembly code must refer to values that are defined by macros, see the chapter about "Using constants" above.あなたのアセンブリコードは、マクロによって定義された値を参照する必要がある場合は、
For a more thorough discussion of inline assembly usage, see the gcc user manual.インラインアセンブリの使用状況についての全体的な説明については、gccのマニュアルを参照してください。
The latest version of the gcc manual is always available here:
http://gcc.gnu.org/onlinedocs/
Copyright (C) 2007-2013 by Harald Kipp. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation.If you think that something hasn't been explained clearly enough or is even wrong, please let me know.
Date (YMD} | Change | Thanks to |
---|---|---|
2014/02/11 | Fixed the first constant example, where the constant must be an input operand. | spider391Tang |
2013/08/16 | Corrected the example code of specific register usage and added a new pitfall section about the same topic. | Sven Köhler |
2012/03/28 | Corrected the pitfall section about constant parameters and moved to the usage section. | enh |
Added a preprocessor macros pitfall. | ||
Added this history. |