You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Inline assembler code for the memory management package uses the GNU extension of named operands, e.g., "%[foo]". We generally use the "\&" constraint for output operands; otherwise, the compiler assumes that the output operands are set as the very last actions of the code, after all input operands have been used. In such a case the compiler may use the same register for an input operand and an output operand. Using "\&" marks the output operand as "early clobber",i.e., the register is altered early on, perhaps before all the inputs have been used.

We also use the trick of creating a register variable to use as an output only (\=) or input-output (+) operand when we want the compiler to pick which register to use. Sometimes when we do that we want the compiler to avoid r0 because it can't be used as a base register. Then we use the "b" constraint instead of "r", where "b" means "register that can be a base register".

Even if you use "volatile" the compiler will analyze the input operands, output operands and clobber list of each asm() and may reorder two asm() sequences if it determines that they don't depend on one another. The "memory" clobber list item will stop that since it means "updates memory in an unpredictable way". When you throw syncs's and isync's around it's even true. The other way to prevent reordering is to put everything into a single asm().

Example 1. Here we alter the output "msr" early on (in the first instruction, in fact) so we use the ampersand. We also mark it as output-only ("=") instead of in-out ("+") because we don't care what the value is before the asm. The use of "volatile" and "memory" make sure that the compiler won't move instructions across the inline assembler code during the course of optimization. We need that because the inline code is changing the Machine State Register and hence the context in which instructions are executed; the isync instruction just affects reordering by the processor not the compiler. The compiler doesn't look at the instructions inside the asm, just at the lists of inputs, output and clobbers.

	register unsigned msr;
	asm volatile
	  (
	   // Make sure that address translation is disabled.
	   "mfmsr  %[msr]                 \n\t"
	   "rlwimi %[msr],%[zero],0,26,27 \n\t" // Clear translation bits (26-27) in MSR.
	   "mtmsr  %[msr]                 \n\t"
	   // Perform a context sync since we need to wait for translation
	   // to turn off. This will also purge any shadowed TLB entries
	   // which must be done before we turn translation back on.
	   "isync \n\t"
	   // Clear the zone-protection and PID registers.
	   "mtzpr %[zero] \n\t"
	   "mtpid %[zero] \n\t"
	   // Mark all TLB entries as invalid.
	   "tlbia"
	   : [msr]"=&r"(msr)
	   : [zero]"r"(0)
	   : "memory"
	   );

Example 2. This code changes a specific memory location so it uses the "m" constraint; the compiler will replace "%[saved]" in the instruction with the correct addressing mode, e.g., base + offset. Note that if necessary the compiler will generate extra code to load the value of "this" into a base register.

      register unsigned oldmsr;
      register unsigned newmsr;
      asm volatile
	(
	 // Save the exception-enabling bits of the current MSR value.
	 "mfmsr %[oldmsr]                   \n\t"
	 "and   %[newmsr],%[oldmsr],%[mask] \n\t"
	 "stw   %[newmsr],%[save]           \n\t"
	 // Reset those enabling bits in the MSR.
	 "andc  %[newmsr],%[oldmsr],%[mask] \n\t"
	 "mtmsr %[newmsr]                   \n\t"
	 // Perform a context sync since the MSR value may have changed.
	 "isync"
	 :[save]"=m"(this->mSavedExcBits), [oldmsr]"=&r"(oldmsr), [newmsr]"=&r"(newmsr)
	 :[mask]"r"(ExceptionBitMask)
	 :
	 );

Example 3. Here's a case where we explicitly specify the contents of a base register and so have to use the "b" constraint to keep the compiler from picking r0. We also put a block of memory on the output list by dereferencing a pointer to the block; in this case we're altering the entire object so we use "*this".

      register unsigned tmp;
      asm volatile
	(// Read and store a Translation Lookaside Buffer entry from the MMU.
          // High part.
	 "tlbrehi %[tmp],%[idx]      \n\t"
	 "stw     %[tmp],0(%[self])  \n\t"
          // Low part.
	 "tlbrelo %[tmp],%[idx]      \n\t"
	 "stw     %[tmp],4(%[self])"
	 : "=m"(*this), [tmp]"=&r"(tmp)
	 : [idx]"r"(this->index), [self]"b"(this)
	 :
	 );
  • No labels