[PATCH] D107611: [ARC] Add codegen for llvm.ctlz intrinsic for the ARC backend

2021-08-05 Thread Thomas Johnson via Phabricator via cfe-commits
thomasjohns created this revision.
thomasjohns added a reviewer: marksl.
Herald added a subscriber: hiraditya.
thomasjohns requested review of this revision.
Herald added projects: clang, LLVM.
Herald added a subscriber: cfe-commits.

Add a CTLZ pseudo instruction to tablegen and the ability to expand this pseudo 
instruction instruction to ARC assembly code.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D107611

Files:
  clang/lib/Basic/Targets/ARC.h
  llvm/lib/Target/ARC/ARCExpandPseudos.cpp
  llvm/lib/Target/ARC/ARCISelLowering.cpp
  llvm/lib/Target/ARC/ARCInstrInfo.td
  llvm/test/CodeGen/ARC/ctlz.ll

Index: llvm/test/CodeGen/ARC/ctlz.ll
===
--- /dev/null
+++ llvm/test/CodeGen/ARC/ctlz.ll
@@ -0,0 +1,14 @@
+; RUN: llc -march=arc < %s | FileCheck %s
+
+target triple = "arc"
+
+declare i32 @llvm.ctlz.i32(i32, i1)
+
+; CHECK-LABEL: clz32:
+; CHECK: fls.f   %r0, %r0
+; CHECK: mov.eq  [[R:%r[01]]], 32
+; CHECK: rsub.ne [[R]], [[R]], 31
+define i32 @clz32(i32 %x) {
+  %a = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
+  ret i32 %a
+}
Index: llvm/lib/Target/ARC/ARCInstrInfo.td
===
--- llvm/lib/Target/ARC/ARCInstrInfo.td
+++ llvm/lib/Target/ARC/ARCInstrInfo.td
@@ -311,6 +311,13 @@
 let Predicates=[HasNorm] in {
   defm NORM  : ArcUnaryEXT5Inst<0b01,"norm">;
   defm NORMH : ArcUnaryEXT5Inst<0b001000,"normh">;
+
+// TODO: Add `Requires<[HasBitScan]>` predicate when available.
+def CTLZ : PseudoInstARC<(outs GPR32:$A),
+ (ins GPR32:$B),
+ "error.fls $A, $B",
+ [(set GPR32:$A, (ctlz i32:$B))]> {
+  let Defs = [STATUS32];
 }
 
 // General Unary Instruction fragments.
Index: llvm/lib/Target/ARC/ARCISelLowering.cpp
===
--- llvm/lib/Target/ARC/ARCISelLowering.cpp
+++ llvm/lib/Target/ARC/ARCISelLowering.cpp
@@ -135,6 +135,10 @@
 
   // Sign extend inreg
   setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Custom);
+
+  // TODO: Predicate with `options.hasBitScan() ? Legal : Expand` when
+  //   the HasBitScan predicate is available.
+  setOperationAction(ISD::CTLZ, MVT::i32, Legal);
 }
 
 const char *ARCTargetLowering::getTargetNodeName(unsigned Opcode) const {
Index: llvm/lib/Target/ARC/ARCExpandPseudos.cpp
===
--- llvm/lib/Target/ARC/ARCExpandPseudos.cpp
+++ llvm/lib/Target/ARC/ARCExpandPseudos.cpp
@@ -13,6 +13,7 @@
 #include "ARCInstrInfo.h"
 #include "ARCRegisterInfo.h"
 #include "ARCSubtarget.h"
+#include "MCTargetDesc/ARCInfo.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -35,6 +36,7 @@
 
 private:
   void ExpandStore(MachineFunction &, MachineBasicBlock::iterator);
+  void ExpandCTLZ(MachineFunction &, MachineBasicBlock::iterator);
 
   const ARCInstrInfo *TII;
 };
@@ -73,10 +75,45 @@
   SI.eraseFromParent();
 }
 
+void ARCExpandPseudos::ExpandCTLZ(MachineFunction &MF,
+  MachineBasicBlock::iterator SII) {
+  // Expand:
+  //	%R2 = CTLZ %R0, %STATUS
+  // To:
+  //	%R2 = FLS_f_rr %R0, %STATUS
+  //	%R2 = MOV_cc_ru6 %R2, 32, pred:1, %STATUS
+  //	%R2 = RSUB_cc_rru6 %R2, 31, pred:2, %STATUS
+  MachineInstr &SI = *SII;
+  const MachineOperand &Dest = SI.getOperand(0);
+  assert(Dest.isReg());
+  const MachineOperand &Src = SI.getOperand(1);
+  assert(Src.isReg());
+  unsigned DestReg = Dest.getReg();
+
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::FLS_f_rr),
+  DestReg)
+  .add(Src)
+  .addReg(ARC::STATUS32, RegState::ImplicitDefine);
+
+  unsigned Ra = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  unsigned Rb = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::MOV_cc_ru6), Ra)
+  .addImm(32)
+  .addImm(ARCCC::EQ)
+  .addReg(DestReg);
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::RSUB_cc_rru6),
+  Rb)
+  .addImm(31)
+  .addImm(ARCCC::NE)
+  .addReg(Ra);
+
+  SI.eraseFromParent();
+}
+
 bool ARCExpandPseudos::runOnMachineFunction(MachineFunction &MF) {
   const ARCSubtarget *STI = &MF.getSubtarget();
   TII = STI->getInstrInfo();
-  bool ExpandedStore = false;
+  bool Expanded = false;
   for (auto &MBB : MF) {
 MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
 while (MBBI != E) {
@@ -86,7 +123,11 @@
   case ARC::STH_FAR:
   case ARC::STB_FAR:
 ExpandStore(MF, MBBI);
-ExpandedStore = true;
+Expanded = true;
+break;
+  case ARC::CTLZ:
+ExpandCTLZ(MF, MBBI);
+Expanded = true;
 break;
   default:
 break;
@@ -94,7 +135,7 @@
   MBBI = NMBBI;
 }
   }
-  return ExpandedStore;
+  re

[PATCH] D107611: [ARC] Add codegen for llvm.ctlz intrinsic for the ARC backend

2021-08-05 Thread Thomas Johnson via Phabricator via cfe-commits
thomasjohns added inline comments.



Comment at: llvm/lib/Target/ARC/ARCInstrInfo.td:314
   defm NORMH : ArcUnaryEXT5Inst<0b001000,"normh">;
+
+// TODO: Add `Requires<[HasBitScan]>` predicate when available.

I just realized I made a bad merge here and dropped the `}`. Fixing it now.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D107611/new/

https://reviews.llvm.org/D107611

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D107611: [ARC] Add codegen for llvm.ctlz intrinsic for the ARC backend

2021-08-05 Thread Thomas Johnson via Phabricator via cfe-commits
thomasjohns added inline comments.



Comment at: llvm/lib/Target/ARC/ARCInstrInfo.td:314
   defm NORMH : ArcUnaryEXT5Inst<0b001000,"normh">;
+
+// TODO: Add `Requires<[HasBitScan]>` predicate when available.

thomasjohns wrote:
> I just realized I made a bad merge here and dropped the `}`. Fixing it now.
I'll also move this pseudo instruction to be next to the other pseudo 
instructions


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D107611/new/

https://reviews.llvm.org/D107611

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D107611: [ARC] Add codegen for llvm.ctlz intrinsic for the ARC backend

2021-08-05 Thread Thomas Johnson via Phabricator via cfe-commits
thomasjohns updated this revision to Diff 364657.
thomasjohns added a comment.

Fix missing `}` after bad merge. Move CTLZ next to other pseudo instructions.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D107611/new/

https://reviews.llvm.org/D107611

Files:
  clang/lib/Basic/Targets/ARC.h
  llvm/lib/Target/ARC/ARCExpandPseudos.cpp
  llvm/lib/Target/ARC/ARCISelLowering.cpp
  llvm/lib/Target/ARC/ARCInstrInfo.td
  llvm/test/CodeGen/ARC/ctlz.ll

Index: llvm/test/CodeGen/ARC/ctlz.ll
===
--- /dev/null
+++ llvm/test/CodeGen/ARC/ctlz.ll
@@ -0,0 +1,14 @@
+; RUN: llc -march=arc < %s | FileCheck %s
+
+target triple = "arc"
+
+declare i32 @llvm.ctlz.i32(i32, i1)
+
+; CHECK-LABEL: clz32:
+; CHECK: fls.f   %r0, %r0
+; CHECK: mov.eq  [[R:%r[01]]], 32
+; CHECK: rsub.ne [[R]], [[R]], 31
+define i32 @clz32(i32 %x) {
+  %a = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
+  ret i32 %a
+}
Index: llvm/lib/Target/ARC/ARCInstrInfo.td
===
--- llvm/lib/Target/ARC/ARCInstrInfo.td
+++ llvm/lib/Target/ARC/ARCInstrInfo.td
@@ -133,6 +133,14 @@
  "STB_FAR $dst, $addr",
  [(truncstorei8 GPR32:$dst, AddrModeFar:$addr)]>;
 
+// TODO: Add `Requires<[HasBitScan]>` predicate when available.
+def CTLZ : PseudoInstARC<(outs GPR32:$A),
+ (ins GPR32:$B),
+ "error.fls $A, $B",
+ [(set GPR32:$A, (ctlz i32:$B))]> {
+  let Defs = [STATUS32];
+}
+
 //===--===//
 // Instruction Generation multiclasses.
 // Generate many variants of a single instruction with a single defining
Index: llvm/lib/Target/ARC/ARCISelLowering.cpp
===
--- llvm/lib/Target/ARC/ARCISelLowering.cpp
+++ llvm/lib/Target/ARC/ARCISelLowering.cpp
@@ -135,6 +135,10 @@
 
   // Sign extend inreg
   setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Custom);
+
+  // TODO: Predicate with `options.hasBitScan() ? Legal : Expand` when
+  //   the HasBitScan predicate is available.
+  setOperationAction(ISD::CTLZ, MVT::i32, Legal);
 }
 
 const char *ARCTargetLowering::getTargetNodeName(unsigned Opcode) const {
Index: llvm/lib/Target/ARC/ARCExpandPseudos.cpp
===
--- llvm/lib/Target/ARC/ARCExpandPseudos.cpp
+++ llvm/lib/Target/ARC/ARCExpandPseudos.cpp
@@ -13,6 +13,7 @@
 #include "ARCInstrInfo.h"
 #include "ARCRegisterInfo.h"
 #include "ARCSubtarget.h"
+#include "MCTargetDesc/ARCInfo.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -35,6 +36,7 @@
 
 private:
   void ExpandStore(MachineFunction &, MachineBasicBlock::iterator);
+  void ExpandCTLZ(MachineFunction &, MachineBasicBlock::iterator);
 
   const ARCInstrInfo *TII;
 };
@@ -73,10 +75,45 @@
   SI.eraseFromParent();
 }
 
+void ARCExpandPseudos::ExpandCTLZ(MachineFunction &MF,
+  MachineBasicBlock::iterator SII) {
+  // Expand:
+  //	%R2 = CTLZ %R0, %STATUS
+  // To:
+  //	%R2 = FLS_f_rr %R0, %STATUS
+  //	%R2 = MOV_cc_ru6 %R2, 32, pred:1, %STATUS
+  //	%R2 = RSUB_cc_rru6 %R2, 31, pred:2, %STATUS
+  MachineInstr &SI = *SII;
+  const MachineOperand &Dest = SI.getOperand(0);
+  assert(Dest.isReg());
+  const MachineOperand &Src = SI.getOperand(1);
+  assert(Src.isReg());
+  unsigned DestReg = Dest.getReg();
+
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::FLS_f_rr),
+  DestReg)
+  .add(Src)
+  .addReg(ARC::STATUS32, RegState::ImplicitDefine);
+
+  unsigned Ra = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  unsigned Rb = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::MOV_cc_ru6), Ra)
+  .addImm(32)
+  .addImm(ARCCC::EQ)
+  .addReg(DestReg);
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::RSUB_cc_rru6),
+  Rb)
+  .addImm(31)
+  .addImm(ARCCC::NE)
+  .addReg(Ra);
+
+  SI.eraseFromParent();
+}
+
 bool ARCExpandPseudos::runOnMachineFunction(MachineFunction &MF) {
   const ARCSubtarget *STI = &MF.getSubtarget();
   TII = STI->getInstrInfo();
-  bool ExpandedStore = false;
+  bool Expanded = false;
   for (auto &MBB : MF) {
 MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
 while (MBBI != E) {
@@ -86,7 +123,11 @@
   case ARC::STH_FAR:
   case ARC::STB_FAR:
 ExpandStore(MF, MBBI);
-ExpandedStore = true;
+Expanded = true;
+break;
+  case ARC::CTLZ:
+ExpandCTLZ(MF, MBBI);
+Expanded = true;
 break;
   default:
 break;
@@ -94,7 +135,7 @@
   MBBI = NMBBI;
 }
   }
-  return ExpandedStore;
+  return Expanded;
 }
 
 F

[PATCH] D107611: [ARC] Add codegen for llvm.ctlz intrinsic for the ARC backend

2021-08-06 Thread Thomas Johnson via Phabricator via cfe-commits
thomasjohns updated this revision to Diff 364806.
thomasjohns added a comment.

llvm clang-tidy rule: prefer `Register` over `unsinged int`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D107611/new/

https://reviews.llvm.org/D107611

Files:
  clang/lib/Basic/Targets/ARC.h
  llvm/lib/Target/ARC/ARCExpandPseudos.cpp
  llvm/lib/Target/ARC/ARCISelLowering.cpp
  llvm/lib/Target/ARC/ARCInstrInfo.td
  llvm/test/CodeGen/ARC/ctlz.ll

Index: llvm/test/CodeGen/ARC/ctlz.ll
===
--- /dev/null
+++ llvm/test/CodeGen/ARC/ctlz.ll
@@ -0,0 +1,14 @@
+; RUN: llc -march=arc < %s | FileCheck %s
+
+target triple = "arc"
+
+declare i32 @llvm.ctlz.i32(i32, i1)
+
+; CHECK-LABEL: clz32:
+; CHECK: fls.f   %r0, %r0
+; CHECK: mov.eq  [[R:%r[01]]], 32
+; CHECK: rsub.ne [[R]], [[R]], 31
+define i32 @clz32(i32 %x) {
+  %a = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
+  ret i32 %a
+}
Index: llvm/lib/Target/ARC/ARCInstrInfo.td
===
--- llvm/lib/Target/ARC/ARCInstrInfo.td
+++ llvm/lib/Target/ARC/ARCInstrInfo.td
@@ -133,6 +133,14 @@
  "STB_FAR $dst, $addr",
  [(truncstorei8 GPR32:$dst, AddrModeFar:$addr)]>;
 
+// TODO: Add `Requires<[HasBitScan]>` predicate when available.
+def CTLZ : PseudoInstARC<(outs GPR32:$A),
+ (ins GPR32:$B),
+ "error.fls $A, $B",
+ [(set GPR32:$A, (ctlz i32:$B))]> {
+  let Defs = [STATUS32];
+}
+
 //===--===//
 // Instruction Generation multiclasses.
 // Generate many variants of a single instruction with a single defining
Index: llvm/lib/Target/ARC/ARCISelLowering.cpp
===
--- llvm/lib/Target/ARC/ARCISelLowering.cpp
+++ llvm/lib/Target/ARC/ARCISelLowering.cpp
@@ -135,6 +135,10 @@
 
   // Sign extend inreg
   setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Custom);
+
+  // TODO: Predicate with `options.hasBitScan() ? Legal : Expand` when
+  //   the HasBitScan predicate is available.
+  setOperationAction(ISD::CTLZ, MVT::i32, Legal);
 }
 
 const char *ARCTargetLowering::getTargetNodeName(unsigned Opcode) const {
Index: llvm/lib/Target/ARC/ARCExpandPseudos.cpp
===
--- llvm/lib/Target/ARC/ARCExpandPseudos.cpp
+++ llvm/lib/Target/ARC/ARCExpandPseudos.cpp
@@ -13,6 +13,7 @@
 #include "ARCInstrInfo.h"
 #include "ARCRegisterInfo.h"
 #include "ARCSubtarget.h"
+#include "MCTargetDesc/ARCInfo.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -35,6 +36,7 @@
 
 private:
   void ExpandStore(MachineFunction &, MachineBasicBlock::iterator);
+  void ExpandCTLZ(MachineFunction &, MachineBasicBlock::iterator);
 
   const ARCInstrInfo *TII;
 };
@@ -59,8 +61,8 @@
 void ARCExpandPseudos::ExpandStore(MachineFunction &MF,
MachineBasicBlock::iterator SII) {
   MachineInstr &SI = *SII;
-  unsigned AddrReg = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
-  unsigned AddOpc =
+  Register AddrReg = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  Register AddOpc =
   isUInt<6>(SI.getOperand(2).getImm()) ? ARC::ADD_rru6 : ARC::ADD_rrlimm;
   BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(AddOpc), AddrReg)
   .addReg(SI.getOperand(1).getReg())
@@ -73,10 +75,45 @@
   SI.eraseFromParent();
 }
 
+void ARCExpandPseudos::ExpandCTLZ(MachineFunction &MF,
+  MachineBasicBlock::iterator SII) {
+  // Expand:
+  //	%R2 = CTLZ %R0, %STATUS
+  // To:
+  //	%R2 = FLS_f_rr %R0, %STATUS
+  //	%R2 = MOV_cc_ru6 %R2, 32, pred:1, %STATUS
+  //	%R2 = RSUB_cc_rru6 %R2, 31, pred:2, %STATUS
+  MachineInstr &SI = *SII;
+  const MachineOperand &Dest = SI.getOperand(0);
+  assert(Dest.isReg());
+  const MachineOperand &Src = SI.getOperand(1);
+  assert(Src.isReg());
+  Register DestReg = Dest.getReg();
+
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::FLS_f_rr),
+  DestReg)
+  .add(Src)
+  .addReg(ARC::STATUS32, RegState::ImplicitDefine);
+
+  Register Ra = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  Register Rb = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::MOV_cc_ru6), Ra)
+  .addImm(32)
+  .addImm(ARCCC::EQ)
+  .addReg(DestReg);
+  BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(ARC::RSUB_cc_rru6),
+  Rb)
+  .addImm(31)
+  .addImm(ARCCC::NE)
+  .addReg(Ra);
+
+  SI.eraseFromParent();
+}
+
 bool ARCExpandPseudos::runOnMachineFunction(MachineFunction &MF) {
   const ARCSubtarget *STI = &MF.getSubtarget();
   TII = STI->getInstrInfo();
-  bool ExpandedStore = 

[PATCH] D107611: [ARC] Add codegen for llvm.ctlz intrinsic for the ARC backend

2021-08-06 Thread Thomas Johnson via Phabricator via cfe-commits
thomasjohns updated this revision to Diff 364855.
thomasjohns added a comment.

Thanks for the feedback, Mark.

This change prefers `MI` over `SI` naming, updates instruction writing, and 
makes the test case more precise (the generated code was improved and no longer 
had an unneeded `mov %r1, %r0`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D107611/new/

https://reviews.llvm.org/D107611

Files:
  clang/lib/Basic/Targets/ARC.h
  llvm/lib/Target/ARC/ARCExpandPseudos.cpp
  llvm/lib/Target/ARC/ARCISelLowering.cpp
  llvm/lib/Target/ARC/ARCInstrInfo.td
  llvm/test/CodeGen/ARC/ctlz.ll

Index: llvm/test/CodeGen/ARC/ctlz.ll
===
--- /dev/null
+++ llvm/test/CodeGen/ARC/ctlz.ll
@@ -0,0 +1,14 @@
+; RUN: llc -march=arc < %s | FileCheck %s
+
+target triple = "arc"
+
+declare i32 @llvm.ctlz.i32(i32, i1)
+
+; CHECK-LABEL: clz32:
+; CHECK:   fls.f   %r0, %r0
+; CHECK-NEXT:  mov.eq  %r0, 32
+; CHECK-NEXT:  rsub.ne %r0, %r0, 31
+define i32 @clz32(i32 %x) {
+  %a = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
+  ret i32 %a
+}
Index: llvm/lib/Target/ARC/ARCInstrInfo.td
===
--- llvm/lib/Target/ARC/ARCInstrInfo.td
+++ llvm/lib/Target/ARC/ARCInstrInfo.td
@@ -133,6 +133,14 @@
  "STB_FAR $dst, $addr",
  [(truncstorei8 GPR32:$dst, AddrModeFar:$addr)]>;
 
+// TODO: Add `Requires<[HasBitScan]>` predicate when available.
+def CTLZ : PseudoInstARC<(outs GPR32:$A),
+ (ins GPR32:$B),
+ "error.fls $A, $B",
+ [(set GPR32:$A, (ctlz i32:$B))]> {
+  let Defs = [STATUS32];
+}
+
 //===--===//
 // Instruction Generation multiclasses.
 // Generate many variants of a single instruction with a single defining
Index: llvm/lib/Target/ARC/ARCISelLowering.cpp
===
--- llvm/lib/Target/ARC/ARCISelLowering.cpp
+++ llvm/lib/Target/ARC/ARCISelLowering.cpp
@@ -135,6 +135,10 @@
 
   // Sign extend inreg
   setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Custom);
+
+  // TODO: Predicate with `options.hasBitScan() ? Legal : Expand` when
+  //   the HasBitScan predicate is available.
+  setOperationAction(ISD::CTLZ, MVT::i32, Legal);
 }
 
 const char *ARCTargetLowering::getTargetNodeName(unsigned Opcode) const {
Index: llvm/lib/Target/ARC/ARCExpandPseudos.cpp
===
--- llvm/lib/Target/ARC/ARCExpandPseudos.cpp
+++ llvm/lib/Target/ARC/ARCExpandPseudos.cpp
@@ -13,6 +13,7 @@
 #include "ARCInstrInfo.h"
 #include "ARCRegisterInfo.h"
 #include "ARCSubtarget.h"
+#include "MCTargetDesc/ARCInfo.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
@@ -35,6 +36,7 @@
 
 private:
   void ExpandStore(MachineFunction &, MachineBasicBlock::iterator);
+  void ExpandCTLZ(MachineFunction &, MachineBasicBlock::iterator);
 
   const ARCInstrInfo *TII;
 };
@@ -59,8 +61,8 @@
 void ARCExpandPseudos::ExpandStore(MachineFunction &MF,
MachineBasicBlock::iterator SII) {
   MachineInstr &SI = *SII;
-  unsigned AddrReg = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
-  unsigned AddOpc =
+  Register AddrReg = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  Register AddOpc =
   isUInt<6>(SI.getOperand(2).getImm()) ? ARC::ADD_rru6 : ARC::ADD_rrlimm;
   BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(AddOpc), AddrReg)
   .addReg(SI.getOperand(1).getReg())
@@ -73,10 +75,39 @@
   SI.eraseFromParent();
 }
 
+void ARCExpandPseudos::ExpandCTLZ(MachineFunction &MF,
+  MachineBasicBlock::iterator MII) {
+  // Expand:
+  //	%R2 = CTLZ %R0, %STATUS
+  // To:
+  //	%R2 = FLS_f_rr %R0, %STATUS
+  //	%R2 = MOV_cc_ru6 %R2, 32, pred:1, %STATUS
+  //	%R2 = RSUB_cc_rru6 %R2, 31, pred:2, %STATUS
+  MachineInstr &MI = *MII;
+  const MachineOperand &Dest = MI.getOperand(0);
+  const MachineOperand &Src = MI.getOperand(1);
+  Register Ra = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+  Register Rb = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass);
+
+  BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(ARC::FLS_f_rr), Ra)
+  .add(Src);
+  BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(ARC::MOV_cc_ru6), Rb)
+  .addImm(32)
+  .addImm(ARCCC::EQ)
+  .addReg(Ra);
+  BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(ARC::RSUB_cc_rru6))
+  .add(Dest)
+  .addImm(31)
+  .addImm(ARCCC::NE)
+  .addReg(Rb);
+
+  MI.eraseFromParent();
+}
+
 bool ARCExpandPseudos::runOnMachineFunction(MachineFunction &MF) {
   const ARCSubtarget *STI = &MF.getSubtarget();
   TII = ST