<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
{font-family:"Malgun Gothic";
panose-1:2 11 5 3 2 0 0 2 0 4;}
@font-face
{font-family:"\@Malgun Gothic";}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
span.EmailStyle17
{mso-style-type:personal-compose;
font-family:"Calibri",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-family:"Calibri",sans-serif;}
@page WordSection1
{size:612.0pt 792.0pt;
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
{page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="#0563C1" vlink="#954F72" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal">Hi All,<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I have a question about handling zero_extend on AArch64 target. Let’s see a simple IR snippet.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">define i32 @jumptable(i32 %in) {<o:p></o:p></p>
<p class="MsoNormal"> switch i32 %in, label %def [<o:p></o:p></p>
<p class="MsoNormal"> i32 0, label %BB1<o:p></o:p></p>
<p class="MsoNormal"> i32 1, label %BB2<o:p></o:p></p>
<p class="MsoNormal"> i32 2, label %BB3<o:p></o:p></p>
<p class="MsoNormal"> i32 3, label %BB4<o:p></o:p></p>
<p class="MsoNormal"> ]<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">def:<o:p></o:p></p>
<p class="MsoNormal"> ret i32 0<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">BB1:<o:p></o:p></p>
<p class="MsoNormal"> ret i32 1<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">BB2:<o:p></o:p></p>
<p class="MsoNormal"> ret i32 2<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">BB3:<o:p></o:p></p>
<p class="MsoNormal"> ret i32 3<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">BB4:<o:p></o:p></p>
<p class="MsoNormal"> ret i32 4<o:p></o:p></p>
<p class="MsoNormal">}<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">It has a simple jump table. If we compile it with llc, the assembly output is as below.<o:p></o:p></p>
<p class="MsoNormal">llc -mtriple=aarch64-unknown ./jump-table.ll -o - -aarch64-enable-atomic-cfg-tidy=0<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">jumptable: // @jumptable<o:p></o:p></p>
<p class="MsoNormal"> .cfi_startproc<o:p></o:p></p>
<p class="MsoNormal">// %bb.0:<o:p></o:p></p>
<p class="MsoNormal"> cmp w0, #3<o:p></o:p></p>
<p class="MsoNormal"> b.hi .LBB0_3<o:p></o:p></p>
<p class="MsoNormal">// %bb.1:<o:p></o:p></p>
<p class="MsoNormal"> adrp x9, .LJTI0_0<o:p></o:p></p>
<p class="MsoNormal"> mov w8, w0<o:p></o:p></p>
<p class="MsoNormal"> add x9, x9, :lo12:.LJTI0_0<o:p></o:p></p>
<p class="MsoNormal"> adr x10, .LBB0_2<o:p></o:p></p>
<p class="MsoNormal"> ldrb w11, [x9, x8]<o:p></o:p></p>
<p class="MsoNormal"> add x10, x10, x11, lsl #2<o:p></o:p></p>
<p class="MsoNormal"> br x10<o:p></o:p></p>
<p class="MsoNormal">.LBB0_2: // %BB1<o:p></o:p></p>
<p class="MsoNormal"> mov w0, #1<o:p></o:p></p>
<p class="MsoNormal"> ret<o:p></o:p></p>
<p class="MsoNormal">.LBB0_3: // %def<o:p></o:p></p>
<p class="MsoNormal"> mov w0, wzr<o:p></o:p></p>
<p class="MsoNormal"> ret<o:p></o:p></p>
<p class="MsoNormal">.LBB0_4: // %BB2<o:p></o:p></p>
<p class="MsoNormal"> mov w0, #2<o:p></o:p></p>
<p class="MsoNormal"> ret<o:p></o:p></p>
<p class="MsoNormal">.LBB0_5: // %BB3<o:p></o:p></p>
<p class="MsoNormal"> mov w0, #3<o:p></o:p></p>
<p class="MsoNormal"> ret<o:p></o:p></p>
<p class="MsoNormal">.LBB0_6: // %BB4<o:p></o:p></p>
<p class="MsoNormal"> mov w0, #4<o:p></o:p></p>
<p class="MsoNormal"> ret<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">From the output, we can see redundant `mov w8, w0` and the zero_extend on ISelDAG causes it as below.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Initial selection DAG: %bb.0 'jumptable:'<o:p></o:p></p>
<p class="MsoNormal">SelectionDAG has 12 nodes:<o:p></o:p></p>
<p class="MsoNormal"> t0: ch = EntryToken<o:p></o:p></p>
<p class="MsoNormal"> t2: i32,ch = CopyFromReg t0, Register:i32 %0<o:p></o:p></p>
<p class="MsoNormal"> t3: i32 = Constant<0><o:p></o:p></p>
<p class="MsoNormal"> t4: i64 = zero_extend t2<o:p></o:p></p>
<p class="MsoNormal"> t6: ch = CopyToReg t0, Register:i64 %1, t4<o:p></o:p></p>
<p class="MsoNormal"> t9: i32 = setcc t2, Constant:i32<3>, setugt:ch<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt">t11: ch = brcond t6, t9, BasicBlock:ch<def 0x560850bfd1a8><o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"><o:p> </o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt">SelectionDAGBuilder’s visitJumpTableHeader generates the `t4: i64 = zero_extend t2`. The MIR from ISelDAG is as below.<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"><o:p> </o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt">===== Instruction selection ends:<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt">Selected selection DAG: %bb.0 'jumptable:'<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt">SelectionDAG has 17 nodes:<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t0: ch = EntryToken<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t2: i32,ch = CopyFromReg t0, Register:i32 %0<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t21: i32 = ORRWrs Register:i32 $wzr, t2, TargetConstant:i32<0><o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t4: i64 = SUBREG_TO_REG TargetConstant:i32<0>, t21, TargetConstant:i32<13><o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t6: ch = CopyToReg t0, Register:i64 %1, t4<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t13: i32,i32 = SUBSWri t2, TargetConstant:i32<3>, TargetConstant:i32<0><o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t18: ch,glue = CopyToReg t6, Register:i32 $nzcv, t13:1<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:4.8pt"> t15: ch = Bcc TargetConstant:i32<8>, BasicBlock:ch<def 0x560850bfd1a8>, t18, t18:1<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">As you can see, the zero_extend is matched to `ORRWrs` and `SUBREG_TO_REG` using below TableGen pattern.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">// When we need to explicitly zero-extend, we use a 32-bit MOV instruction and<o:p></o:p></p>
<p class="MsoNormal">// then assert the extension has happened.<o:p></o:p></p>
<p class="MsoNormal">def : Pat<(i64 (zext GPR32:$src)),<o:p></o:p></p>
<p class="MsoNormal"> (SUBREG_TO_REG (i32 0), (ORRWrs WZR, GPR32:$src, 0), sub_32)>;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">The `ORRWrs` is mapped to mov instruction. In order to avoid it, below pattern could be selected.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">def def32 : PatLeaf<(i32 GPR32:$src), [{<o:p></o:p></p>
<p class="MsoNormal"> return isDef32(*N);<o:p></o:p></p>
<p class="MsoNormal">}]>;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">// In the case of a 32-bit def that is known to implicitly zero-extend,<o:p></o:p></p>
<p class="MsoNormal">// we can use a SUBREG_TO_REG.<o:p></o:p></p>
<p class="MsoNormal">def : Pat<(i64 (zext def32:$src)), (SUBREG_TO_REG (i64 0), GPR32:$src, sub_32)>;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">However, the isDef32 from the def32 constraint returns false for `CopyFromReg` because it could need explicit zero extend. If we can check the CopyFromReg’s src operand, we could decide whether the `CopyFromReg` needs explicit zero extend
or not… However, its src operand is located in other basic block and we can not see it on ISelDAG level. How can we remove the redundant `mov` instruction from zero_extend well? It could be a case of peephole optimization to fold the `ORRWrs` and `SUBREG_TO_REG`
in MIR level? I guess AArch64 folks have seen redundant `mov` instruction from zero_extend from various cases as well as jumptable… If I missed something, please let me know.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Thanks<o:p></o:p></p>
<p class="MsoNormal">JinGu Kang<o:p></o:p></p>
</div>
</body>
</html>