aboutsummaryrefslogtreecommitdiff
path: root/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/macro_assembler_arith_impl.h
blob: cc5ce510b5db94c29f8a0d92518612278c5de4d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_
#define RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_

#include <climits>
#include <type_traits>

#include "berberis/base/bit_util.h"
#include "berberis/intrinsics/macro_assembler.h"

namespace berberis {

// Divisor comes in "src", dividend comes in gpr_a, result is returned in gpr_a.
// gpr_d and FLAGS are clobbered by that macroinstruction.
template <typename Assembler>
template <typename IntType>
void MacroAssembler<Assembler>::MacroDiv(Register src) {
  Label* zero = MakeLabel();
  Label* done = MakeLabel();
  Test<IntType>(src, src);
  Jcc(Condition::kZero, *zero);

  if constexpr (std::is_signed_v<IntType>) {
    Label* do_idiv = MakeLabel();
    // If min int32_t/int64_t is divided by -1 then in risc-v the result is
    // the dividend, but x86 will raise an exception. Handle this case separately.
    Cmp<IntType>(src, int8_t{-1});
    Jcc(Condition::kNotEqual, *do_idiv);

    if constexpr (std::is_same_v<IntType, int64_t>) {
      Cmp<IntType>(gpr_a,
                   {.disp = constants_pool::kVectorConst<std::numeric_limits<IntType>::min()>});
    } else {
      Cmp<IntType>(gpr_a, std::numeric_limits<IntType>::min());
    }
    Jcc(Condition::kEqual, *done);

    Bind(do_idiv);
    // If we are dealing with 8-bit signed case then we need to sign-extend %al into %ax.
    if constexpr (std::is_same_v<IntType, int8_t>) {
      Cbw();
    // We need to sign-extend gpr_a into gpr_d to ensure 32bit/64-bit/128-bit dividend is correct.
    } else if constexpr (std::is_same_v<IntType, int16_t>) {
      Cwd();
    } else if constexpr (std::is_same_v<IntType, int32_t>) {
      Cdq();
    } else if constexpr (std::is_same_v<IntType, int64_t>) {
      Cqo();
    } else {
      static_assert(kDependentTypeFalse<IntType>, "Unsupported format");
    }
  } else if constexpr (std::is_same_v<IntType, uint8_t>) {
    // For 8bit unsigned case we need “xor %ah, %ah” instruction, but our assembler doesn't support
    // %ah register. Use .byte to emit the required machine code.
    TwoByte(uint16_t{0xe430});
  } else {
    // We need to zero-extend eax into dx/edx/rdx to ensure 32-bit/64-bit/128-bit dividend is
    // correct.
    Xor<uint32_t>(gpr_d, gpr_d);
  }

  Div<IntType>(src);
  Jmp(*done);

  Bind(zero);
  Mov<IntType>(gpr_a, int64_t{-1});

  Bind(done);
}
}  // namespace berberis

#endif  // RISCV64_TO_X86_64_BERBERIS_INTRINSICS_MACRO_ASSEMBLER_ARITH_IMPL_H_