arm gcc inline assembler cookbook
arm gcc inline assembler cookbookand save one ARM instruction. As a result: There is no guarantee, that the compiled code will retain the sequence of statements given in the source codeThis 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 modifiedby an interrupt routine. Disabling interrupts before accessing the variables and re-enable them afterwards looks like a good ideaasm volatile(mrs rl2, cosr\n\tnorr r12, r12, #OxCO\n\tmsr csr_Cr r12\n\t"r12asm volatile('mrs r12, cosr\nbic r12, r12, #Oxco\n");Unfortunately the optimizer may decide to do the multiplication first and then execute both inline assembler instructions or vice versa. This will make ourassembly code uselessWe can solve this with the help of the clobber list, which will be explained now. The clobber list from the example aboveforms the compiler that the assembly code modifies register r12 and updates the condition code flags. Btw. using a hard coded register will ty pically preventbest optimization results. In general you should pass a variable and let the compiler choose the adequate register. beside register names and cc for thecondition register, memory is a valid keyword too. It tells the compiler that the assembler instruction may change memory locations. this forces the compiler tostore all cached values before and reload them after executing the assembler instructions. And it must retain the sequence, because the contents of allvariables is unpredictable after executing an asm statement with a memory clobber.asm volatile(mrs r12, cosr\n\t"ory12,r12,#0xC0\nt"nmsrC.Ac*= b; / This is safe. x/asm volatile(mrs rl2, cosr\n'bic r12, r12, #0xco\n"msr cosr c, r12"r12","c:c:"," memoy")Invalidating all cached values may be suboptimal. Alternatively you can add a dummy operand to create an artificial dependencyasn volatile("mrs r12, cosr\n\tnorr r12, r12, #OxCO\n\t'n"msr cosr c, r12\n\t":"-X"(b)asm v:latile(mrs r12, cosr\n"bicx12,r12,#0xC0\n""msr cosr c, r12::"X"(c):"r12","cc")iThis code pretends to modify variable b in the first asm statement and to use the contents variable c in the second. This will preserve the sequence of ourthree statements without invalidating other cached variablesIt is essential to understand how the optimizer affects inline assembler statements If something remains nebulous better re -read this part before moving onhe the next topicInput and output operandsWe learned, that each input and output operand is described by a symbolic name enclosed in square bracket followed by a constraint string which in turn isfollowed by a C expression in parenthesesWhat are these constraints and why do we need them? You probably know that every assembly instruction accepts specific operand types only. For examplethe 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 onlyIn 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 theinline assembler may be the same C function pointer. Thus, when passing constants pointers or variables to inline assembly statements, the in line assemblermust know, how they should be represented in the assembly codeFor ARM processors, GCC 4 provides the following constraintsConstraint Usage in ARM stateUsage in Thumb stateFloating point registers fo. f7Not availableNot availableRegisters r8.[15Immediate floating point constantNot availableHame a G, but negatedNot availableImmediate value in data processing instructions Constant in the range 0.255e.g. ORR RO, RO, #operandSWi operandIndexing constants-4095. 4095Constant in the range..-1e.g. LDR Rl, [PC, #operandg SUB RO, RO, #operandKSame as I, but invertedSame as i but sh iftedle as I, but negatedConstant in the range -7..7e.g. SUB RO, Rl, #operandSame as rRegisters ro.r7e.g. PUSH operandConstant in the range of 0.. 32 or a power of 2 Constant that is a multiple of 4 in the range of 0..1020e.g. MOV R2 R1 ROR #operandADD RO, SP,#operandAny valid memory addressNot availableConstant in the range of.. 31e.g. LSL RO, R1, #operandNot availableConstant that is a multiple of 4 in the range of -508. 508e. g. ADD SP, #operandGeneral register ro.r15Not availablee.g. SUB operand1, operand2, operand 3Vector floating point registers so. s31Not availableAny operandConstraint characters may be prepended by a single constraint modifier. Constraints without a modifier specify read -only operands, Modifiers areModifier specifieWrite -only operand, usually used for all output operandsRead-write operand, must be listed as an output operandA register that should be used for output onlyOutput operands must be write-only and the c expression result must be an Ivalue which means that the operands must be valid on the left side ofassignments. The C compiler is able to check thisnput operands are, you guessed it read-only. Note that the c com piler will not be able to check whether the operands are of reasonable type for the kindof operation used in the assembler instructions. Most problems will be detected during the late assembly stage which is well known for its weird errormessages. Even if it claims to have found an internal compiler problem that should be immediately reported to the authors, you better check your inlineassem bler code firstA 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 asshown in the next exampleasm("mov [value, gValue, ror t1": valuel +r"(y))his 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 previousexample, the result is not stored in another variable. Instead the original contents of in put variable will be modifiedThe modifier may not be supported by earlier releases of the com piler. 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("movC80,ror#⊥":"=r"(va⊥ue)value))iConstraint"tells the compiler, to use the same input register that is used for the first output operandNote 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 doso. You may remember the first assembly listing of the rotating bits example with two variables, where the compiler used the same register r3 for bothvariables, the asm statementasm ("mov [result],o[value], ror #1":[esult] =r"(y):value]"r"(x))generated this code00309DE51dx-3,[sp,#0]x;xE330ACE1 movr r3, ror #1 tm04308DE5 str 23, [spr #4] a tmp, yThis 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 situationwhere your code depends on different registers used for input and output operands, you must add the constraint modifier to your output operand. ne onsfollowing code demonstrates this problemasIm volatile("ldr 90, [1]str82,[1,#4]""n、t"Tr(&table),"r"(wdvnmInA 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 inputand output, then the output value would have been destroyed on the first assembler instruction. Fortunately the modifier instructs the compiler not toselect any register for the output value, which is used for any of the input operands.More recipesInline assembler as preprocessor macroIn order to reuse your assembler language parts, it is useful to define them as macros and put them into include files. Using such include files may producecompiler 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 volatilenstead 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#defire BYTESWAP(val)\asmvolatile (16\n、="rbicr3,r3,#0×00FF0000\n、"是0,80,x3,1sr#8"r(val)\C stub functionsMacro definitions will include the same assembler code whenever they are referenced This may not be acceptable for larger routines. In this case you maydefine a C stub function. Here is the byte swap procedure again this time implemented as a C functionunsigned long Byte Swap (ur signed long val)asm volatileMeor16r3,r3,+0×03FF0000\n-是0,81,roY#8\n=",0,r3,1srt8"r(val)yreturn valReplacing symbolic names of C variablesBy default GCC uses the same symbolic names of functions or variables in C and assembler code. you can specify a different name for the assembler code byusing a special form of the asm statement:unsigned long value asm("clock")=3686400This statement instructs the compiler to use the symbolic name clock rather than value. This makes sense only for global variables. Local variables(aka autovariables) do not have symbolic names in assembler code.Replacing symbolic names of C functionsIn order to change the name of a function, you need a prototy pe declaration, because the compiler will not accept the asm keyword in the function definitionextern long Calc(void) asm ("CALCULATE")iCalling the function CalcO will create assembler instructions to call the function CALCULATEForcing usage of specific registersA local variable may be held in a register. you can instruct the inline assembler to use a specific register for it.register unsigned char counter asm("r3)isoime ccdeasm volatile(eor 23, r3, r3)imore ccdeThe assembler instruction, eor r3, r3, r3"will clear the variable counter. Be warned, that this sample is bad in most situations, because it interferes with thecompiler's optimizer. Furthermore, GCC will not completely reserve the specified register. If the optimizer recognizes that the variable will not be referencedany 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 reservetoo many registers in this way, the compiler may even run out of registers during code generationUsing registers temporarilyIf 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 amodified by the ands instruction and thus cc had been added to the clobbers ut this by specifying r3 in the clobber list. Furthermore the CPU status flags aremultiple of four. It uses r3 as a scratch register and lets the compiler know abasm volatile(0,是0,.\t各0,井4”gain, hard coding register usage is always bad coding style. Better implement a C stub function and use a local variable for temporary valuesRegister UsageIt is always a good idea to analyze the assembly listing output of the c compiler and study the generated code. the following ta ble of the compilers typicalregister usage will be probably helpful to understand the codeRegister Alt Name UsageFirst function argumentnteger function resultScratch registerSecond function argumentScratch register3Third function argumentScratch registera4Scratch register. rgumentFourth function1Register variablev2 Register variableRegister variablev4Register variablerBv5Register variableRegister variableReal frame pointerr10Stack limitr11Argument pointer12Temporary workspaceStack pointer14Link registerWorkspacer15 pcProgram counterCommon pitfallsInstruction sequenceDevelopers often expect, that a sequence of instructions remains in the final code as specified in the source code. This assumption is wrong and oftenintroduces hard to find bugs. Actually, asm statements are processed by the optimizer in the same way as other c statements. They may be rearranged ifdependencies allow thisThe chapter C code optimization" discusses the details and offers solutionsExecuting in Thumb statusBe aware, that, depending on the given compile options, the compiler may switch to thumb state. Using inline assembler with instructions that are notavailable in thumb state will result in cryptic compile errorsAssembly code sizeIn most cases the compiler will correctly determine the size of the assembler instruction but it may become confused by assembler macros. Better avoid themIn case you are confused: This is about assembly language macros, not C preprocessor macros. It is fine to use the latterLabelsithin the assembler instruction you can use labels as jump targets. However, you must not jump from one assembler instruction into another. The optimizerknows nothing about those branches and may generate bad codeExternal linksFor a more thorough discussion of inline assembly usage, see the gcc user manual. the latest version of the gcc manual is always available herehttp://gcc.gnu.org/onlinedocs/CopyrightAs you may (or may not) know all original work is subject to a copyright, even if not explicitly stated. The previous version of this document had been copieddocument under the following copyrg.. ome copies claim however, that the publisher(not me) is the original author(grumble). So I decided to publish thisand re-published often, which is great. SoCopyrigh-()2007-2009 by Harald Kippunder the terms of the GNU Free Documen-ation Licer se, Version 1./t[ssrs granted to copy, distribute and/cr modi fy this documeor any later version published by the free Software Foundation
用户评论
很好,正好是我想要得