[llvm] e858f51 - [InstCombine] Remove assumptions about int having 32 bits

Martin Sebor via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 16 14:36:20 PDT 2022


Author: Martin Sebor
Date: 2022-08-16T15:35:08-06:00
New Revision: e858f5120da77f46a6676c6fd63ccbbb83b23d2d

URL: https://github.com/llvm/llvm-project/commit/e858f5120da77f46a6676c6fd63ccbbb83b23d2d
DIFF: https://github.com/llvm/llvm-project/commit/e858f5120da77f46a6676c6fd63ccbbb83b23d2d.diff

LOG: [InstCombine] Remove assumptions about int having 32 bits

Reviewed By: bjope

Differential Revision: https://reviews.llvm.org/D131731

Added: 
    llvm/test/Transforms/InstCombine/ffs-i16.ll
    llvm/test/Transforms/InstCombine/fls-i16.ll
    llvm/test/Transforms/InstCombine/isascii-i16.ll
    llvm/test/Transforms/InstCombine/isdigit-i16.ll
    llvm/test/Transforms/InstCombine/printf-i16.ll
    llvm/test/Transforms/InstCombine/puts-i16.ll

Modified: 
    llvm/lib/Transforms/Utils/BuildLibCalls.cpp
    llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
index 048f132f174b..b15e14b93ea8 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -1756,22 +1756,19 @@ Value *llvm::emitBinaryFloatFnCall(Value *Op1, Value *Op2,
   return emitBinaryFloatFnCallHelper(Op1, Op2, TheLibFunc, Name, B, Attrs, TLI);
 }
 
+// Emit a call to putchar(int) with Char as the argument.  Char must have
+// the same precision as int, which need not be 32 bits.
 Value *llvm::emitPutChar(Value *Char, IRBuilderBase &B,
                          const TargetLibraryInfo *TLI) {
   Module *M = B.GetInsertBlock()->getModule();
   if (!isLibFuncEmittable(M, TLI, LibFunc_putchar))
     return nullptr;
 
+  Type *Ty = Char->getType();
   StringRef PutCharName = TLI->getName(LibFunc_putchar);
-  FunctionCallee PutChar = getOrInsertLibFunc(M, *TLI, LibFunc_putchar,
-                                              B.getInt32Ty(), B.getInt32Ty());
+  FunctionCallee PutChar = getOrInsertLibFunc(M, *TLI, LibFunc_putchar, Ty, Ty);
   inferNonMandatoryLibFuncAttrs(M, PutCharName, *TLI);
-  CallInst *CI = B.CreateCall(PutChar,
-                              B.CreateIntCast(Char,
-                              B.getInt32Ty(),
-                              /*isSigned*/true,
-                              "chari"),
-                              PutCharName);
+  CallInst *CI = B.CreateCall(PutChar, Char, PutCharName);
 
   if (const Function *F =
           dyn_cast<Function>(PutChar.getCallee()->stripPointerCasts()))

diff  --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 3552215502c7..30f95cddff73 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2550,21 +2550,24 @@ void LibCallSimplifier::classifyArgUse(
 //===----------------------------------------------------------------------===//
 
 Value *LibCallSimplifier::optimizeFFS(CallInst *CI, IRBuilderBase &B) {
-  // ffs(x) -> x != 0 ? (i32)llvm.cttz(x)+1 : 0
+  // All variants of ffs return int which need not be 32 bits wide.
+  // ffs{,l,ll}(x) -> x != 0 ? (int)llvm.cttz(x)+1 : 0
+  Type *RetType = CI->getType();
   Value *Op = CI->getArgOperand(0);
   Type *ArgType = Op->getType();
   Function *F = Intrinsic::getDeclaration(CI->getCalledFunction()->getParent(),
                                           Intrinsic::cttz, ArgType);
   Value *V = B.CreateCall(F, {Op, B.getTrue()}, "cttz");
   V = B.CreateAdd(V, ConstantInt::get(V->getType(), 1));
-  V = B.CreateIntCast(V, B.getInt32Ty(), false);
+  V = B.CreateIntCast(V, RetType, false);
 
   Value *Cond = B.CreateICmpNE(Op, Constant::getNullValue(ArgType));
-  return B.CreateSelect(Cond, V, B.getInt32(0));
+  return B.CreateSelect(Cond, V, ConstantInt::get(RetType, 0));
 }
 
 Value *LibCallSimplifier::optimizeFls(CallInst *CI, IRBuilderBase &B) {
-  // fls(x) -> (i32)(sizeInBits(x) - llvm.ctlz(x, false))
+  // All variants of fls return int which need not be 32 bits wide.
+  // fls{,l,ll}(x) -> (int)(sizeInBits(x) - llvm.ctlz(x, false))
   Value *Op = CI->getArgOperand(0);
   Type *ArgType = Op->getType();
   Function *F = Intrinsic::getDeclaration(CI->getCalledFunction()->getParent(),
@@ -2587,15 +2590,17 @@ Value *LibCallSimplifier::optimizeAbs(CallInst *CI, IRBuilderBase &B) {
 Value *LibCallSimplifier::optimizeIsDigit(CallInst *CI, IRBuilderBase &B) {
   // isdigit(c) -> (c-'0') <u 10
   Value *Op = CI->getArgOperand(0);
-  Op = B.CreateSub(Op, B.getInt32('0'), "isdigittmp");
-  Op = B.CreateICmpULT(Op, B.getInt32(10), "isdigit");
+  Type *ArgType = Op->getType();
+  Op = B.CreateSub(Op, ConstantInt::get(ArgType, '0'), "isdigittmp");
+  Op = B.CreateICmpULT(Op, ConstantInt::get(ArgType, 10), "isdigit");
   return B.CreateZExt(Op, CI->getType());
 }
 
 Value *LibCallSimplifier::optimizeIsAscii(CallInst *CI, IRBuilderBase &B) {
   // isascii(c) -> c <u 128
   Value *Op = CI->getArgOperand(0);
-  Op = B.CreateICmpULT(Op, B.getInt32(128), "isascii");
+  Type *ArgType = Op->getType();
+  Op = B.CreateICmpULT(Op, ConstantInt::get(ArgType, 128), "isascii");
   return B.CreateZExt(Op, CI->getType());
 }
 
@@ -2701,9 +2706,15 @@ Value *LibCallSimplifier::optimizePrintFString(CallInst *CI, IRBuilderBase &B) {
   if (!CI->use_empty())
     return nullptr;
 
+  Type *IntTy = CI->getType();
   // printf("x") -> putchar('x'), even for "%" and "%%".
-  if (FormatStr.size() == 1 || FormatStr == "%%")
-    return copyFlags(*CI, emitPutChar(B.getInt32(FormatStr[0]), B, TLI));
+  if (FormatStr.size() == 1 || FormatStr == "%%") {
+    // Convert the character to unsigned char before passing it to putchar
+    // to avoid host-specific sign extension in the IR.  Putchar converts
+    // it to unsigned char regardless.
+    Value *IntChar = ConstantInt::get(IntTy, (unsigned char)FormatStr[0]);
+    return copyFlags(*CI, emitPutChar(IntChar, B, TLI));
+  }
 
   // Try to remove call or emit putchar/puts.
   if (FormatStr == "%s" && CI->arg_size() > 1) {
@@ -2714,8 +2725,13 @@ Value *LibCallSimplifier::optimizePrintFString(CallInst *CI, IRBuilderBase &B) {
     if (OperandStr.empty())
       return (Value *)CI;
     // printf("%s", "a") --> putchar('a')
-    if (OperandStr.size() == 1)
-      return copyFlags(*CI, emitPutChar(B.getInt32(OperandStr[0]), B, TLI));
+    if (OperandStr.size() == 1) {
+      // Convert the character to unsigned char before passing it to putchar
+      // to avoid host-specific sign extension in the IR.  Putchar converts
+      // it to unsigned char regardless.
+      Value *IntChar = ConstantInt::get(IntTy, (unsigned char)OperandStr[0]);
+      return copyFlags(*CI, emitPutChar(IntChar, B, TLI));
+    }
     // printf("%s", str"\n") --> puts(str)
     if (OperandStr.back() == '\n') {
       OperandStr = OperandStr.drop_back();
@@ -2738,8 +2754,12 @@ Value *LibCallSimplifier::optimizePrintFString(CallInst *CI, IRBuilderBase &B) {
   // Optimize specific format strings.
   // printf("%c", chr) --> putchar(chr)
   if (FormatStr == "%c" && CI->arg_size() > 1 &&
-      CI->getArgOperand(1)->getType()->isIntegerTy())
-    return copyFlags(*CI, emitPutChar(CI->getArgOperand(1), B, TLI));
+      CI->getArgOperand(1)->getType()->isIntegerTy()) {
+    // Convert the argument to the type expected by putchar, i.e., int, which
+    // need not be 32 bits wide but which is the same as printf's return type.
+    Value *IntChar = B.CreateIntCast(CI->getArgOperand(1), IntTy, false);
+    return copyFlags(*CI, emitPutChar(IntChar, B, TLI));
+  }
 
   // printf("%s\n", str) --> puts(str)
   if (FormatStr == "%s\n" && CI->arg_size() > 1 &&
@@ -3192,8 +3212,12 @@ Value *LibCallSimplifier::optimizePuts(CallInst *CI, IRBuilderBase &B) {
   // Check for a constant string.
   // puts("") -> putchar('\n')
   StringRef Str;
-  if (getConstantStringInfo(CI->getArgOperand(0), Str) && Str.empty())
-    return copyFlags(*CI, emitPutChar(B.getInt32('\n'), B, TLI));
+  if (getConstantStringInfo(CI->getArgOperand(0), Str) && Str.empty()) {
+    // putchar takes an argument of the same type as puts returns, i.e.,
+    // int, which need not be 32 bits wide.
+    Type *IntTy = CI->getType();
+    return copyFlags(*CI, emitPutChar(ConstantInt::get(IntTy, '\n'), B, TLI));
+  }
 
   return nullptr;
 }

diff  --git a/llvm/test/Transforms/InstCombine/ffs-i16.ll b/llvm/test/Transforms/InstCombine/ffs-i16.ll
new file mode 100644
index 000000000000..0984724511ef
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/ffs-i16.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+;
+; Test that the ffs library call simplifier works correctly even for
+; targets with 16-bit int.
+;
+; RUN: opt < %s -mtriple=avr-linux -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -mtriple=msp430-linux -passes=instcombine -S | FileCheck %s
+
+declare i16 @ffs(i16)
+
+declare void @sink(i16)
+
+
+define void @fold_ffs(i16 %x) {
+; CHECK-LABEL: @fold_ffs(
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    [[CTTZ:%.*]] = call i16 @llvm.cttz.i16(i16 [[X:%.*]], i1 true), !range [[RNG0:![0-9]+]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add nuw nsw i16 [[CTTZ]], 1
+; CHECK-NEXT:    [[DOTNOT:%.*]] = icmp eq i16 [[X]], 0
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[DOTNOT]], i16 0, i16 [[TMP1]]
+; CHECK-NEXT:    call void @sink(i16 [[TMP2]])
+; CHECK-NEXT:    ret void
+;
+  %n0 = call i16 @ffs(i16 0)
+  call void @sink(i16 %n0)
+
+  %n1 = call i16 @ffs(i16 1)
+  call void @sink(i16 %n1)
+
+  %nx = call i16 @ffs(i16 %x)
+  call void @sink(i16 %nx)
+
+  ret void
+}

diff  --git a/llvm/test/Transforms/InstCombine/fls-i16.ll b/llvm/test/Transforms/InstCombine/fls-i16.ll
new file mode 100644
index 000000000000..83f3dbd33946
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fls-i16.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+;
+; Test that the fls library call simplifier works correctly even for
+; targets with 16-bit int.  Although fls is available on a number of
+; targets it's supported (hardcoded as available) only on FreeBSD.
+;
+; RUN: opt < %s -mtriple=avr-freebsd -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -mtriple=msp430-freebsd -passes=instcombine -S | FileCheck %s
+
+declare i16 @fls(i16)
+
+declare void @sink(i16)
+
+
+define void @fold_fls(i16 %x) {
+; CHECK-LABEL: @fold_fls(
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    [[CTLZ:%.*]] = call i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false), !range [[RNG0:![0-9]+]]
+; CHECK-NEXT:    [[TMP1:%.*]] = sub nuw nsw i16 16, [[CTLZ]]
+; CHECK-NEXT:    call void @sink(i16 [[TMP1]])
+; CHECK-NEXT:    ret void
+;
+  %n0 = call i16 @fls(i16 0)
+  call void @sink(i16 %n0)
+
+  %n1 = call i16 @fls(i16 1)
+  call void @sink(i16 %n1)
+
+  %nx = call i16 @fls(i16 %x)
+  call void @sink(i16 %nx)
+
+  ret void
+}

diff  --git a/llvm/test/Transforms/InstCombine/isascii-i16.ll b/llvm/test/Transforms/InstCombine/isascii-i16.ll
new file mode 100644
index 000000000000..131906d798a3
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/isascii-i16.ll
@@ -0,0 +1,57 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Test that the isascii library call simplifier works correctly even for
+; targets with 16-bit int.
+;
+; RUN: opt < %s -mtriple=avr-freebsd -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -mtriple=msp430-linux -passes=instcombine -S | FileCheck %s
+
+declare i16 @isascii(i16)
+
+declare void @sink(i16)
+
+
+define void @fold_isascii(i16 %c) {
+; CHECK-LABEL: @fold_isascii(
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    [[ISASCII:%.*]] = icmp ult i16 [[C:%.*]], 128
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i1 [[ISASCII]] to i16
+; CHECK-NEXT:    call void @sink(i16 [[TMP1]])
+; CHECK-NEXT:    ret void
+;
+  %i0 = call i16 @isascii(i16 0)
+  call void @sink(i16 %i0)
+
+  %i1 = call i16 @isascii(i16 1)
+  call void @sink(i16 %i1)
+
+  %i127 = call i16 @isascii(i16 127)
+  call void @sink(i16 %i127)
+
+  %i128 = call i16 @isascii(i16 128)
+  call void @sink(i16 %i128)
+
+  %i255 = call i16 @isascii(i16 255)
+  call void @sink(i16 %i255)
+
+  %i256 = call i16 @isascii(i16 256)
+  call void @sink(i16 %i256)
+
+  ; Fold isascii(INT_MAX) to 0.  The call is valid with all int values.
+  %imax = call i16 @isascii(i16 32767)
+  call void @sink(i16 %imax)
+
+  %uimax = call i16 @isascii(i16 65535)
+  call void @sink(i16 %uimax)
+
+  %ic = call i16 @isascii(i16 %c)
+  call void @sink(i16 %ic)
+
+  ret void
+}

diff  --git a/llvm/test/Transforms/InstCombine/isdigit-i16.ll b/llvm/test/Transforms/InstCombine/isdigit-i16.ll
new file mode 100644
index 000000000000..35c58c282a8a
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/isdigit-i16.ll
@@ -0,0 +1,82 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Test that the isdigit library call simplifier works correctly even for
+; targets with 16-bit int.
+;
+; RUN: opt < %s -mtriple=avr-linux -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -mtriple=msp430-freebsd -passes=instcombine -S | FileCheck %s
+
+declare i16 @isdigit(i16)
+
+declare void @sink(i16)
+
+define void @fold_isdigit(i16 %c) {
+; CHECK-LABEL: @fold_isdigit(
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    call void @sink(i16 1)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    call void @sink(i16 0)
+; CHECK-NEXT:    [[ISDIGITTMP:%.*]] = add i16 [[C:%.*]], -48
+; CHECK-NEXT:    [[ISDIGIT:%.*]] = icmp ult i16 [[ISDIGITTMP]], 10
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i1 [[ISDIGIT]] to i16
+; CHECK-NEXT:    call void @sink(i16 [[TMP1]])
+; CHECK-NEXT:    ret void
+;
+  %i0 = call i16 @isdigit(i16 0)
+  call void @sink(i16 %i0)
+
+  %i1 = call i16 @isdigit(i16 1)
+  call void @sink(i16 %i1)
+
+  ; Fold isdigit('/') to 0.
+  %i47 = call i16 @isdigit(i16 47)
+  call void @sink(i16 %i47)
+
+; Fold isdigit('0') to 1.
+  %i48 = call i16 @isdigit(i16 48)
+  call void @sink(i16 %i48)
+
+  ; Fold isdigit('1') to 1.
+  %i49 = call i16 @isdigit(i16 49)
+  call void @sink(i16 %i49)
+
+  ; Fold isdigit('9') to 1.
+  %i57 = call i16 @isdigit(i16 57)
+  call void @sink(i16 %i57)
+
+  ; Fold isdigit(':') to 0.
+  %i58 = call i16 @isdigit(i16 58)
+  call void @sink(i16 %i58)
+
+  %i127 = call i16 @isdigit(i16 127)
+  call void @sink(i16 %i127)
+
+  %i128 = call i16 @isdigit(i16 128)
+  call void @sink(i16 %i128)
+
+  %i255 = call i16 @isdigit(i16 255)
+  call void @sink(i16 %i255)
+
+  ; Fold isdigit(256) to 0.  The argument is required to be representable
+  ; in unsigned char but it's a common mistake to call the function with
+  ; other arguments and it's arguably safer to fold such calls than to
+  ; let the library call return an arbitrary value or crash.
+  %i256 = call i16 @isdigit(i16 256)
+  call void @sink(i16 %i256)
+
+  ; Same as above.
+  %imax = call i16 @isdigit(i16 32767)
+  call void @sink(i16 %imax)
+
+  %ic = call i16 @isdigit(i16 %c)
+  call void @sink(i16 %ic)
+
+  ret void
+}

diff  --git a/llvm/test/Transforms/InstCombine/printf-i16.ll b/llvm/test/Transforms/InstCombine/printf-i16.ll
new file mode 100644
index 000000000000..96609a87889d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/printf-i16.ll
@@ -0,0 +1,72 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+;
+; RUN: opt < %s -mtriple=avr-freebsd -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -mtriple=msp430-linux -passes=instcombine -S | FileCheck %s
+;
+; Verify that the puts to putchar transformation works correctly even for
+; targets with 16-bit int.
+
+declare i16 @putchar(i16)
+declare i16 @puts(i8*)
+
+ at s1 = constant [2 x i8] c"\01\00"
+ at s7f = constant [2 x i8] c"\7f\00"
+ at s80 = constant [2 x i8] c"\80\00"
+ at sff = constant [2 x i8] c"\ff\00"
+
+ at pcnt_c = constant [3 x i8] c"%c\00"
+ at pcnt_s = constant [3 x i8] c"%s\00"
+
+declare i16 @printf(i8*, ...)
+
+; Verfify that the three printf to putchar transformations all result
+; in the same output for calls with equivalent arguments.
+
+define void @xform_printf(i8 %c8, i16 %c16) {
+; CHECK-LABEL: @xform_printf(
+; CHECK-NEXT:    [[PUTCHAR:%.*]] = call i16 @putchar(i16 1)
+; CHECK-NEXT:    [[PUTCHAR1:%.*]] = call i16 @putchar(i16 1)
+; CHECK-NEXT:    [[PUTCHAR2:%.*]] = call i16 @putchar(i16 1)
+; CHECK-NEXT:    [[PUTCHAR3:%.*]] = call i16 @putchar(i16 127)
+; CHECK-NEXT:    [[PUTCHAR4:%.*]] = call i16 @putchar(i16 127)
+; CHECK-NEXT:    [[PUTCHAR5:%.*]] = call i16 @putchar(i16 127)
+; CHECK-NEXT:    [[PUTCHAR6:%.*]] = call i16 @putchar(i16 128)
+; CHECK-NEXT:    [[PUTCHAR7:%.*]] = call i16 @putchar(i16 128)
+; CHECK-NEXT:    [[PUTCHAR8:%.*]] = call i16 @putchar(i16 128)
+; CHECK-NEXT:    [[PUTCHAR9:%.*]] = call i16 @putchar(i16 255)
+; CHECK-NEXT:    [[PUTCHAR10:%.*]] = call i16 @putchar(i16 255)
+; CHECK-NEXT:    [[PUTCHAR11:%.*]] = call i16 @putchar(i16 255)
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[C8:%.*]] to i16
+; CHECK-NEXT:    [[PUTCHAR12:%.*]] = call i16 @putchar(i16 [[TMP1]])
+; CHECK-NEXT:    [[PUTCHAR13:%.*]] = call i16 @putchar(i16 [[C16:%.*]])
+; CHECK-NEXT:    ret void
+;
+  %ppcnt_c = getelementptr [3 x i8], [3 x i8]* @pcnt_c, i32 0, i32 0
+  %ppcnt_s = getelementptr [3 x i8], [3 x i8]* @pcnt_s, i32 0, i32 0
+
+  %ps1 = getelementptr [2 x i8], [2 x i8]* @s1, i32 0, i32 0
+  call i16 (i8*, ...) @printf(i8* %ps1)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_c, i16 1)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_s, i8* %ps1)
+
+  %ps7f = getelementptr [2 x i8], [2 x i8]* @s7f, i32 0, i32 0
+  call i16 (i8*, ...) @printf(i8* %ps7f)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_c, i16 127)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_s, i8* %ps7f)
+
+  %ps80 = getelementptr [2 x i8], [2 x i8]* @s80, i32 0, i32 0
+  call i16 (i8*, ...) @printf(i8* %ps80)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_c, i16 128)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_s, i8* %ps80)
+
+  %psff = getelementptr [2 x i8], [2 x i8]* @sff, i32 0, i32 0
+  call i16 (i8*, ...) @printf(i8* %psff)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_c, i16 255)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_s, i8* %psff)
+
+; The i8 argument to printf can be either zero-extended or sign-extended
+; when passed to putchar which then converts it to unsigned char.
+  call i16 (i8*, ...) @printf(i8* %ppcnt_c, i8 %c8)
+  call i16 (i8*, ...) @printf(i8* %ppcnt_c, i16 %c16)
+  ret void
+}

diff  --git a/llvm/test/Transforms/InstCombine/puts-i16.ll b/llvm/test/Transforms/InstCombine/puts-i16.ll
new file mode 100644
index 000000000000..fb79fc59c95c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/puts-i16.ll
@@ -0,0 +1,24 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+;
+; RUN: opt < %s -mtriple=avr-linux -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -mtriple=msp430-freebsd -passes=instcombine -S | FileCheck %s
+;
+; Test that the puts to putchar transformation works correctly even for
+; targets with 16-bit int.
+
+declare i16 @putchar(i16)
+declare i16 @puts(i8*)
+
+ at empty = constant [1 x i8] c"\00"
+
+define void @xform_puts(i16 %c) {
+; CHECK-LABEL: @xform_puts(
+; CHECK-NEXT:    [[PUTCHAR:%.*]] = call i16 @putchar(i16 10)
+; CHECK-NEXT:    ret void
+;
+; Transform puts("") to putchar("\n").
+  %s = getelementptr [1 x i8], [1 x i8]* @empty, i32 0, i32 0
+  call i16 @puts(i8* %s)
+
+  ret void
+}


        


More information about the llvm-commits mailing list