[LLVMbugs] [Bug 23389] New: Intrinsics llvm.read_register, llvm.write_register and ISel node types ISD::READ_REGISTER, ISD::WRITE_REGISTER are broken.

bugzilla-daemon at llvm.org bugzilla-daemon at llvm.org
Fri May 1 08:54:53 PDT 2015


https://llvm.org/bugs/show_bug.cgi?id=23389

            Bug ID: 23389
           Summary: Intrinsics llvm.read_register, llvm.write_register and
                    ISel node types ISD::READ_REGISTER,
                    ISD::WRITE_REGISTER are broken.
           Product: new-bugs
           Version: trunk
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P
         Component: new bugs
          Assignee: unassignedbugs at nondot.org
          Reporter: Nicholas.Paul.Johnson at DEShawResearch.com
                CC: llvmbugs at cs.uiuc.edu
    Classification: Unclassified

Created attachment 14275
  --> https://llvm.org/bugs/attachment.cgi?id=14275&action=edit
Testcase of register-asm syntax

(0) Background

I am developing a backend for a custom architecture.  Our ABI reserves a few
registers which we reserve for the application.  Those registers are accessed
from the C++-language by use of the assembly register syntax, like so:

  register int r0 asm("r0");

Clang lowers these accesses in LLVM IR using the llvm.read_register and
llvm.write_register intrinsics.



There is a minor difficulty while reporting this bug, since you do not have the
code for my backend.  Nonetheless, the bug is in the llvm mainline and will
manifest in any backend that exposes registers in this manner.

I'll try to explain this bug with respect to x86.  To do that, we need a minor
change to X86 so that it exposes register r15 at the C language level:

diff --git a/lib/Target/X86/X86ISelLowering.cpp
b/lib/Target/X86/X86ISelLowering.cpp
index 98a19ee..91f2b8d 100644
--- a/lib/Target/X86/X86ISelLowering.cpp
+++ b/lib/Target/X86/X86ISelLowering.cpp
@@ -15598,6 +15598,7 @@ unsigned X86TargetLowering::getRegisterByName(const
char* RegName,
   unsigned Reg = StringSwitch<unsigned>(RegName)
                        .Case("esp", X86::ESP)
                        .Case("rsp", X86::RSP)
+                       .Case("r15", X86::R15)
                        .Default(0);
   if (Reg)
     return Reg;



(1) Symptoms

Compilation gives different results under -O0 and -O3.  To reproduce:

Compile
  clang++ -O0 -c -emit-llvm -S bug.cpp -o bug.o0.ll
  clang++ -O3 -c -emit-llvm -S bug.cpp -o bug.o3.ll


Specifically, bug.o0.ll will re-load the latest value of the register (via
llvm.read_register) before each printf.  From bug.o0.ll:

   ...
   call void @_Z4zerov()
   %0 = call i64 @llvm.read_register.i64(metadata !0)
   store i64 %0, i64* %observation1, align 8
   %1 = load i64, i64* %observation1, align 8
   %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8],
[12 x i8]* @.str, i32 0, i32 0), i64 %1)
   call void @_Z3onev()
   %2 = call i64 @llvm.read_register.i64(metadata !0)
   store i64 %2, i64* %observation2, align 8
   %3 = load i64, i64* %observation2, align 8
   %call1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8],
[13 x i8]* @.str1, i32 0, i32 0), i64 %3)
   ...

In contrast, bug.o3.ll will only read the register once, providing the same
integer argument to each printf.

 ; Function Attrs: nounwind uwtable
 define i32 @main(i32 %argc, i8** nocapture readnone %argv) #0 {
 entry:
   tail call void @llvm.write_register.i64(metadata !0, i64 0) #1
   %0 = tail call i64 @llvm.read_register.i64(metadata !0)
   %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x
i8], [12 x i8]* @.str, i64 0, i64 0), i64 %0)
   %add.i = add i64 %0, 1
   tail call void @llvm.write_register.i64(metadata !0, i64 %add.i) #1
   %call1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x
i8], [13 x i8]* @.str1, i64 0, i64 0), i64 %0)
   ret i32 0
 }



Also note: if you try to compile directly to a binary, clang / llc will crash
with "Invalid child # of SDNode".  I'll discuss that symptom below also.



(2) Analysis

There are several problems:

(2a) The intrinsic llvm.read_register is marked with IntrNoMem.  Consequently,
optimizations treat it as independent of llvm.write_register.  They incorrectly
reorder llvm.read_register w.r.t. llvm.write_register.
==> Instead, llvm.read_register should NOT have the IntrNoMem attribute, just
like llvm.write_register.

(2b) When SelectionDAGBuilder lowers llvm.write_register to
ISD::WRITE_REGISTER, it uses some nonsense value for the SDNode's chain
argument.  This is part of the "Invalid child" problem mentioned above.
==> Instead, the Chain operand should be from DAG.getRoot().

(2c) SelectionDAGISelLowering clobbers the value of the chain operand when it
translates ISD::WRITE_REGISTER into ISD::CopyToReg.
==> In should preserve Op->getOperand(0) rather than replacing it with
CurDAG->getEntryNode().



(3) Patch

I have a patch, but it seems can only submit one attachment at this time.  I'll
post a follow-up with the patch.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20150501/2e7e34e0/attachment.html>


More information about the llvm-bugs mailing list