250 lines
9.6 KiB
C
250 lines
9.6 KiB
C
//===- 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 <stdlib.h>
|
|
// #include <stdint.h>
|
|
//
|
|
// 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 { 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
|
|
}
|
|
)"};
|