//===- LLVMSaddWithOverflow.h - implementation of llvm.sadd.with.overflow -===// // // The LLVM/SPIRV Translator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // // Copyright (c) 2020 Intel Corporation. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal with the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimers in the documentation // and/or other materials provided with the distribution. // Neither the names of Intel Corporation, nor the names of its // contributors may be used to endorse or promote products derived from this // Software without specific prior written permission. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // //===----------------------------------------------------------------------===// // // This file implements lowering of llvm.sadd.with.overflow.* into basic LLVM // operations. // //===----------------------------------------------------------------------===// // The IR below is slightly manually modified IR which was produced by Clang // from the C++ code below. The modifications include: // - adapting the return value, i.e. replacing `store` instructions for the c // and o arguments with `insertvalue` instructions. // - changing type of the `phi` instruction in the last basic block from `i8` // to `i1`. That also requires change of the argument of the `phi` instruction // and allowed to remove an unnecessary `sext` instruction. // // #include // #include // // const unsigned short i16_abs_pos_max = 0x7FFF; // 32767; // const unsigned short i16_abs_neg_max = 0x8000; // 32768; // // void llvm_sadd_with_overflow_i16(int16_t a, int16_t b, int16_t& c, bool& o) { // bool overflow = false; // bool both_pos = (a>=0 && b>=0); // bool both_neg = (a<0 && b<0); // if (both_pos || both_neg) { // // 32-bit integers are always supported in SPIR-V // uint32_t x = (uint32_t)abs(a) + (uint32_t)abs(b); // if (both_pos && x > i16_abs_pos_max || // both_neg && x > i16_abs_neg_max) { // overflow = true; // } // } // c = a + b; // o = overflow; // } // // const uint32_t i32_abs_pos_max = 0x7FFFFFFF; // 2147483647 // const uint32_t i32_abs_neg_max = 0x80000000; // 2147483648 // const int32_t i32_min = 0x80000000; // -2147483648 // // void llvm_sadd_with_overflow_i32(int32_t a, int32_t b, int32_t& c, bool& o) { // bool overflow = false; // bool both_pos = (a>=0 && b>=0); // bool both_neg = (a<0 && b<0); // // if a or b is the most negative number we can't get its absolute value, // // because it is out of range. // if (both_neg && (a == i32_min || b == i32_min)) // overflow = true; // else if (both_pos || both_neg) { // uint32_t x = (uint32_t)abs(a) + (uint32_t)abs(b); // if (both_pos && x > i32_abs_pos_max || // both_neg && x > 2147483648U) { // overflow = true; // } // } // c = a + b; // o = overflow; // } // // const uint64_t i64_abs_pos_max = 0x7fffffffffffffff; // 9223372036854775807 // const uint64_t i64_abs_neg_max = 0x8000000000000000; // 9223372036854775808 // const int64_t i64_min = 0x8000000000000000; // -9223372036854775808 // // void llvm_sadd_with_overflow_i64(int64_t a, int64_t b, int64_t& c, bool& o) { // bool overflow = false; // bool both_pos = (a>=0 && b>=0); // bool both_neg = (a<0 && b<0); // // if a or b is the most negative number we can't get its absolute value, // // because it is out of range. // if (both_neg && (a == i64_min || b == i64_min)) // overflow = true; // else if (both_pos || both_neg) { // uint64_t x = (uint64_t)abs(a) + (uint64_t)abs(b); // if (both_pos && x > i64_abs_pos_max || // both_neg && x > i64_abs_neg_max) { // overflow = true; // } // } // c = a + b; // o = overflow; // } // // const unsigned int abs_pos_max = 2147483647; // const unsigned int abs_neg_max = 2147483648; // // void llvm_sadd_with_overflow_i32(int a, int b, int& c, bool& o) { // bool overflow = false; // bool both_pos = (a>=0 && b>=0); // bool both_neg = (a<0 && b<0); // if (both_pos || both_neg) { // unsigned int x = (unsigned int)abs(a) + (unsigned int)abs(b); // if (both_pos && x > abs_pos_max || // both_neg && x > abs_neg_max) { // overflow = true; // } // } // c = a + b; // o = overflow; // } // Clang options: -emit-llvm -O2 -g0 -fno-discard-value-names static const char LLVMSaddWithOverflow[]{R"( define spir_func { i8, i1 } @llvm_sadd_with_overflow_i8(i8 %a, i8 %b) { entry: %conv = sext i8 %a to i32 %conv1 = sext i8 %b to i32 %0 = or i8 %b, %a %1 = icmp sgt i8 %0, -1 %2 = and i8 %b, %a %3 = icmp slt i8 %2, 0 %brmerge = or i1 %1, %3 br i1 %brmerge, label %if.then, label %if.end21 if.then: ; preds = %entry %4 = icmp slt i32 %conv, 0 %neg = sub nsw i32 0, %conv %5 = select i1 %4, i32 %neg, i32 %conv %6 = icmp slt i32 %conv1, 0 %neg39 = sub nsw i32 0, %conv1 %7 = select i1 %6, i32 %neg39, i32 %conv1 %add = add nuw nsw i32 %7, %5 %cmp15 = icmp ugt i32 %add, 127 %or.cond = and i1 %1, %cmp15 %cmp19 = icmp ugt i32 %add, 128 %or.cond28 = and i1 %3, %cmp19 %or.cond40 = or i1 %or.cond, %or.cond28 br label %if.end21 if.end21: ; preds = %if.then, %entry %overflow = phi i1 [ 0, %entry ], [ %or.cond40, %if.then ] %add24 = add i8 %b, %a %agg = insertvalue {i8, i1} poison, i8 %add24, 0 %res = insertvalue {i8, i1} %agg, i1 %overflow, 1 ret {i8, i1} %res } define spir_func { i16, i1 } @llvm_sadd_with_overflow_i16(i16 %a, i16 %b) { entry: %conv = sext i16 %a to i32 %conv1 = sext i16 %b to i32 %0 = or i16 %b, %a %1 = icmp sgt i16 %0, -1 %2 = and i16 %b, %a %3 = icmp slt i16 %2, 0 %brmerge = or i1 %1, %3 br i1 %brmerge, label %if.then, label %if.end21 if.then: ; preds = %entry %4 = icmp slt i32 %conv, 0 %neg = sub nsw i32 0, %conv %5 = select i1 %4, i32 %neg, i32 %conv %6 = icmp slt i32 %conv1, 0 %neg39 = sub nsw i32 0, %conv1 %7 = select i1 %6, i32 %neg39, i32 %conv1 %add = add nuw nsw i32 %7, %5 %cmp15 = icmp ugt i32 %add, 32767 %or.cond = and i1 %1, %cmp15 %cmp19 = icmp ugt i32 %add, 32768 %or.cond28 = and i1 %3, %cmp19 %or.cond40 = or i1 %or.cond, %or.cond28 br label %if.end21 if.end21: ; preds = %if.then, %entry %overflow = phi i1 [ 0, %entry ], [ %or.cond40, %if.then ] %add24 = add i16 %b, %a %agg = insertvalue {i16, i1} poison, i16 %add24, 0 %res = insertvalue {i16, i1} %agg, i1 %overflow, 1 ret {i16, i1} %res } define spir_func { i32, i1 } @llvm_sadd_with_overflow_i32(i32 %a, i32 %b) { entry: %0 = or i32 %b, %a %1 = icmp sgt i32 %0, -1 %2 = and i32 %b, %a %3 = icmp slt i32 %2, 0 br i1 %3, label %land.lhs.true, label %if.else land.lhs.true: ; preds = %entry %cmp7 = icmp eq i32 %a, -2147483648 %cmp8 = icmp eq i32 %b, -2147483648 %or.cond = or i1 %cmp7, %cmp8 br i1 %or.cond, label %if.end23, label %if.then12 if.else: ; preds = %entry br i1 %1, label %if.then12, label %if.end23 if.then12: ; preds = %land.lhs.true, %if.else %4 = icmp slt i32 %a, 0 %neg = sub nsw i32 0, %a %5 = select i1 %4, i32 %neg, i32 %a %6 = icmp slt i32 %b, 0 %neg42 = sub nsw i32 0, %b %7 = select i1 %6, i32 %neg42, i32 %b %add = add nuw i32 %7, %5 %cmp16 = icmp slt i32 %add, 0 %or.cond27 = and i1 %1, %cmp16 %cmp20 = icmp ugt i32 %add, -2147483648 %or.cond28 = and i1 %3, %cmp20 %or.cond43 = or i1 %or.cond27, %or.cond28 br label %if.end23 if.end23: ; preds = %if.then12, %if.else, %land.lhs.true %overflow = phi i1 [ 1, %land.lhs.true ], [ 0, %if.else ], [ %or.cond43, %if.then12 ] %add24 = add nsw i32 %b, %a %agg = insertvalue {i32, i1} poison, i32 %add24, 0 %res = insertvalue {i32, i1} %agg, i1 %overflow, 1 ret {i32, i1} %res } define spir_func { i64, i1 } @llvm_sadd_with_overflow_i64(i64 %a, i64 %b) { entry: %0 = or i64 %b, %a %1 = icmp sgt i64 %0, -1 %2 = and i64 %b, %a %3 = icmp slt i64 %2, 0 br i1 %3, label %land.lhs.true, label %if.else land.lhs.true: ; preds = %entry %cmp7 = icmp eq i64 %a, -9223372036854775808 %cmp8 = icmp eq i64 %b, -9223372036854775808 %or.cond = or i1 %cmp7, %cmp8 br i1 %or.cond, label %if.end23, label %if.then12 if.else: ; preds = %entry br i1 %1, label %if.then12, label %if.end23 if.then12: ; preds = %land.lhs.true, %if.else %neg.i = sub nsw i64 0, %a %abscond.i = icmp slt i64 %a, 0 %abs.i = select i1 %abscond.i, i64 %neg.i, i64 %a %neg.i43 = sub nsw i64 0, %b %abscond.i44 = icmp slt i64 %b, 0 %abs.i45 = select i1 %abscond.i44, i64 %neg.i43, i64 %b %add = add nuw i64 %abs.i45, %abs.i %cmp16 = icmp slt i64 %add, 0 %or.cond27 = and i1 %1, %cmp16 %cmp20 = icmp ugt i64 %add, -9223372036854775808 %or.cond28 = and i1 %3, %cmp20 %or.cond42 = or i1 %or.cond27, %or.cond28 br label %if.end23 if.end23: ; preds = %if.then12, %if.else, %land.lhs.true %overflow = phi i1 [ 1, %land.lhs.true ], [ 0, %if.else ], [ %or.cond42, %if.then12 ] %add24 = add nsw i64 %b, %a %agg = insertvalue {i64, i1} poison, i64 %add24, 0 %res = insertvalue {i64, i1} %agg, i1 %overflow, 1 ret {i64, i1} %res } )"};