mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-02-06 23:15:16 +00:00
739 lines
25 KiB
C
739 lines
25 KiB
C
/*
|
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
*/
|
|
|
|
#include "simd_conversions.h"
|
|
#include "simd_common.h"
|
|
#include "../aot_emit_exception.h"
|
|
#include "../aot_emit_numberic.h"
|
|
#include "../../aot/aot_runtime.h"
|
|
|
|
static bool
|
|
simd_integer_narrow_x86(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
LLVMTypeRef in_vector_type, LLVMTypeRef out_vector_type,
|
|
const char *intrinsic)
|
|
{
|
|
LLVMValueRef vector1, vector2, result;
|
|
LLVMTypeRef param_types[2] = { in_vector_type, in_vector_type };
|
|
|
|
if (!(vector2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type, "vec2"))
|
|
|| !(vector1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type, "vec1"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(result = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic,
|
|
out_vector_type, param_types, 2,
|
|
vector1, vector2))) {
|
|
HANDLE_FAILURE("LLVMBuildCall");
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
enum integer_sat_type {
|
|
e_sat_i16x8 = 0,
|
|
e_sat_i32x4,
|
|
e_sat_i64x2,
|
|
e_sat_i32x8,
|
|
};
|
|
|
|
static LLVMValueRef
|
|
simd_saturate(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
enum integer_sat_type itype, LLVMValueRef vector,
|
|
LLVMValueRef min, LLVMValueRef max, bool is_signed)
|
|
{
|
|
LLVMValueRef result;
|
|
LLVMTypeRef vector_type;
|
|
|
|
LLVMTypeRef param_types[][2] = {
|
|
{ V128_i16x8_TYPE, V128_i16x8_TYPE },
|
|
{ V128_i32x4_TYPE, V128_i32x4_TYPE },
|
|
{ V128_i64x2_TYPE, V128_i64x2_TYPE },
|
|
{ 0 },
|
|
};
|
|
|
|
const char *smin_intrinsic[] = {
|
|
"llvm.smin.v8i16",
|
|
"llvm.smin.v4i32",
|
|
"llvm.smin.v2i64",
|
|
"llvm.smin.v8i32",
|
|
};
|
|
|
|
const char *umin_intrinsic[] = {
|
|
"llvm.umin.v8i16",
|
|
"llvm.umin.v4i32",
|
|
"llvm.umin.v2i64",
|
|
"llvm.umin.v8i32",
|
|
};
|
|
|
|
const char *smax_intrinsic[] = {
|
|
"llvm.smax.v8i16",
|
|
"llvm.smax.v4i32",
|
|
"llvm.smax.v2i64",
|
|
"llvm.smax.v8i32",
|
|
};
|
|
|
|
const char *umax_intrinsic[] = {
|
|
"llvm.umax.v8i16",
|
|
"llvm.umax.v4i32",
|
|
"llvm.umax.v2i64",
|
|
"llvm.umax.v8i32",
|
|
};
|
|
|
|
if (e_sat_i32x8 == itype) {
|
|
if (!(vector_type = LLVMVectorType(I32_TYPE, 8))) {
|
|
HANDLE_FAILURE("LLVMVectorType");
|
|
return NULL;
|
|
}
|
|
|
|
param_types[itype][0] = vector_type;
|
|
param_types[itype][1] = vector_type;
|
|
}
|
|
|
|
if (!(result = aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx,
|
|
is_signed ? smin_intrinsic[itype] : umin_intrinsic[itype],
|
|
param_types[itype][0], param_types[itype], 2, vector, max))
|
|
|| !(result = aot_call_llvm_intrinsic(
|
|
comp_ctx, func_ctx,
|
|
is_signed ? smax_intrinsic[itype] : umax_intrinsic[itype],
|
|
param_types[itype][0], param_types[itype], 2, result, min))) {
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool
|
|
simd_integer_narrow_common(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
enum integer_sat_type itype, bool is_signed)
|
|
{
|
|
LLVMValueRef vec1, vec2, min, max, mask, result;
|
|
LLVMTypeRef in_vector_type[] = { V128_i16x8_TYPE, V128_i32x4_TYPE,
|
|
V128_i64x2_TYPE };
|
|
LLVMTypeRef min_max_type[] = { INT16_TYPE, I32_TYPE, I64_TYPE };
|
|
LLVMTypeRef trunc_type[3] = { 0 };
|
|
uint8 length[] = { 8, 4, 2 };
|
|
|
|
int64 smin[] = { 0xff80, 0xffFF8000, 0xffFFffFF80000000 };
|
|
int64 umin[] = { 0x0, 0x0, 0x0 };
|
|
int64 smax[] = { 0x007f, 0x00007fff, 0x000000007fFFffFF };
|
|
int64 umax[] = { 0x00ff, 0x0000ffff, 0x00000000ffFFffFF };
|
|
|
|
LLVMValueRef mask_element[] = {
|
|
LLVM_CONST(i32_zero), LLVM_CONST(i32_one),
|
|
LLVM_CONST(i32_two), LLVM_CONST(i32_three),
|
|
LLVM_CONST(i32_four), LLVM_CONST(i32_five),
|
|
LLVM_CONST(i32_six), LLVM_CONST(i32_seven),
|
|
LLVM_CONST(i32_eight), LLVM_CONST(i32_nine),
|
|
LLVM_CONST(i32_ten), LLVM_CONST(i32_eleven),
|
|
LLVM_CONST(i32_twelve), LLVM_CONST(i32_thirteen),
|
|
LLVM_CONST(i32_fourteen), LLVM_CONST(i32_fifteen),
|
|
};
|
|
|
|
if (!(trunc_type[0] = LLVMVectorType(INT8_TYPE, 8))
|
|
|| !(trunc_type[1] = LLVMVectorType(INT16_TYPE, 4))
|
|
|| !(trunc_type[2] = LLVMVectorType(I32_TYPE, 2))) {
|
|
HANDLE_FAILURE("LLVMVectorType");
|
|
return false;
|
|
}
|
|
|
|
if (!(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type[itype], "vec2"))
|
|
|| !(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type[itype], "vec1"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(max = simd_build_splat_const_integer_vector(
|
|
comp_ctx, min_max_type[itype],
|
|
is_signed ? smax[itype] : umax[itype], length[itype]))
|
|
|| !(min = simd_build_splat_const_integer_vector(
|
|
comp_ctx, min_max_type[itype],
|
|
is_signed ? smin[itype] : umin[itype], length[itype]))) {
|
|
return false;
|
|
}
|
|
|
|
/* Refer to:
|
|
* https://github.com/WebAssembly/spec/blob/main/proposals/simd/SIMD.md#integer-to-integer-narrowing
|
|
* Regardless of the whether the operation is signed or unsigned, the input
|
|
* lanes are interpreted as signed integers.
|
|
*/
|
|
if (!(vec1 = simd_saturate(comp_ctx, func_ctx, e_sat_i16x8, vec1, min, max,
|
|
true))
|
|
|| !(vec2 = simd_saturate(comp_ctx, func_ctx, e_sat_i16x8, vec2, min,
|
|
max, true))) {
|
|
return false;
|
|
}
|
|
|
|
/* trunc */
|
|
if (!(vec1 = LLVMBuildTrunc(comp_ctx->builder, vec1, trunc_type[itype],
|
|
"vec1_trunc"))
|
|
|| !(vec2 = LLVMBuildTrunc(comp_ctx->builder, vec2, trunc_type[itype],
|
|
"vec2_trunc"))) {
|
|
HANDLE_FAILURE("LLVMBuildTrunc");
|
|
return false;
|
|
}
|
|
|
|
/* combine */
|
|
if (!(mask = LLVMConstVector(mask_element, (length[itype] << 1)))) {
|
|
HANDLE_FAILURE("LLVMConstInt");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, vec1, vec2, mask,
|
|
"vec_shuffle"))) {
|
|
HANDLE_FAILURE("LLVMBuildShuffleVector");
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i8x16_narrow_i16x8(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_signed)
|
|
{
|
|
if (is_target_x86(comp_ctx)) {
|
|
return simd_integer_narrow_x86(
|
|
comp_ctx, func_ctx, V128_i16x8_TYPE, V128_i8x16_TYPE,
|
|
is_signed ? "llvm.x86.sse2.packsswb.128"
|
|
: "llvm.x86.sse2.packuswb.128");
|
|
}
|
|
else {
|
|
return simd_integer_narrow_common(comp_ctx, func_ctx, e_sat_i16x8,
|
|
is_signed);
|
|
}
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i16x8_narrow_i32x4(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_signed)
|
|
{
|
|
if (is_target_x86(comp_ctx)) {
|
|
return simd_integer_narrow_x86(comp_ctx, func_ctx, V128_i32x4_TYPE,
|
|
V128_i16x8_TYPE,
|
|
is_signed ? "llvm.x86.sse2.packssdw.128"
|
|
: "llvm.x86.sse41.packusdw");
|
|
}
|
|
else {
|
|
return simd_integer_narrow_common(comp_ctx, func_ctx, e_sat_i32x4,
|
|
is_signed);
|
|
}
|
|
}
|
|
|
|
enum integer_extend_type {
|
|
e_ext_i8x16,
|
|
e_ext_i16x8,
|
|
e_ext_i32x4,
|
|
};
|
|
|
|
static LLVMValueRef
|
|
simd_integer_extension(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
enum integer_extend_type itype, LLVMValueRef vector,
|
|
bool lower_half, bool is_signed)
|
|
{
|
|
LLVMValueRef mask, sub_vector, result;
|
|
LLVMValueRef bits[] = {
|
|
LLVM_CONST(i32_zero), LLVM_CONST(i32_one),
|
|
LLVM_CONST(i32_two), LLVM_CONST(i32_three),
|
|
LLVM_CONST(i32_four), LLVM_CONST(i32_five),
|
|
LLVM_CONST(i32_six), LLVM_CONST(i32_seven),
|
|
LLVM_CONST(i32_eight), LLVM_CONST(i32_nine),
|
|
LLVM_CONST(i32_ten), LLVM_CONST(i32_eleven),
|
|
LLVM_CONST(i32_twelve), LLVM_CONST(i32_thirteen),
|
|
LLVM_CONST(i32_fourteen), LLVM_CONST(i32_fifteen),
|
|
};
|
|
LLVMTypeRef out_vector_type[] = { V128_i16x8_TYPE, V128_i32x4_TYPE,
|
|
V128_i64x2_TYPE };
|
|
LLVMValueRef undef[] = { LLVM_CONST(i8x16_undef), LLVM_CONST(i16x8_undef),
|
|
LLVM_CONST(i32x4_undef) };
|
|
uint32 sub_vector_length[] = { 8, 4, 2 };
|
|
|
|
if (!(mask = lower_half ? LLVMConstVector(bits, sub_vector_length[itype])
|
|
: LLVMConstVector(bits + sub_vector_length[itype],
|
|
sub_vector_length[itype]))) {
|
|
HANDLE_FAILURE("LLVMConstVector");
|
|
return false;
|
|
}
|
|
|
|
/* retrieve the low or high half */
|
|
if (!(sub_vector = LLVMBuildShuffleVector(comp_ctx->builder, vector,
|
|
undef[itype], mask, "half"))) {
|
|
HANDLE_FAILURE("LLVMBuildShuffleVector");
|
|
return false;
|
|
}
|
|
|
|
if (is_signed) {
|
|
if (!(result = LLVMBuildSExt(comp_ctx->builder, sub_vector,
|
|
out_vector_type[itype], "sext"))) {
|
|
HANDLE_FAILURE("LLVMBuildSExt");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if (!(result = LLVMBuildZExt(comp_ctx->builder, sub_vector,
|
|
out_vector_type[itype], "zext"))) {
|
|
HANDLE_FAILURE("LLVMBuildZExt");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool
|
|
simd_integer_extension_wrapper(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
enum integer_extend_type itype, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
LLVMValueRef vector, result;
|
|
|
|
LLVMTypeRef in_vector_type[] = { V128_i8x16_TYPE, V128_i16x8_TYPE,
|
|
V128_i32x4_TYPE };
|
|
|
|
if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type[itype], "vec"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(result = simd_integer_extension(comp_ctx, func_ctx, itype, vector,
|
|
lower_half, is_signed))) {
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i16x8_extend_i8x16(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
return simd_integer_extension_wrapper(comp_ctx, func_ctx, e_ext_i8x16,
|
|
lower_half, is_signed);
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i32x4_extend_i16x8(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
return simd_integer_extension_wrapper(comp_ctx, func_ctx, e_ext_i16x8,
|
|
lower_half, is_signed);
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i64x2_extend_i32x4(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
return simd_integer_extension_wrapper(comp_ctx, func_ctx, e_ext_i32x4,
|
|
lower_half, is_signed);
|
|
}
|
|
|
|
static LLVMValueRef
|
|
simd_trunc_sat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
const char *intrinsics, LLVMTypeRef in_vector_type,
|
|
LLVMTypeRef out_vector_type)
|
|
{
|
|
LLVMValueRef vector, result;
|
|
LLVMTypeRef param_types[] = { in_vector_type };
|
|
|
|
if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, in_vector_type,
|
|
"vector"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(result = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsics,
|
|
out_vector_type, param_types, 1,
|
|
vector))) {
|
|
return false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i32x4_trunc_sat_f32x4(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_signed)
|
|
{
|
|
LLVMValueRef result;
|
|
if (!(result = simd_trunc_sat(comp_ctx, func_ctx,
|
|
is_signed ? "llvm.fptosi.sat.v4i32.v4f32"
|
|
: "llvm.fptoui.sat.v4i32.v4f32",
|
|
V128_f32x4_TYPE, V128_i32x4_TYPE))) {
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i32x4_trunc_sat_f64x2(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_signed)
|
|
{
|
|
LLVMValueRef result, zero, mask;
|
|
LLVMTypeRef out_vector_type;
|
|
LLVMValueRef lanes[] = {
|
|
LLVM_CONST(i32_zero),
|
|
LLVM_CONST(i32_one),
|
|
LLVM_CONST(i32_two),
|
|
LLVM_CONST(i32_three),
|
|
};
|
|
|
|
if (!(out_vector_type = LLVMVectorType(I32_TYPE, 2))) {
|
|
HANDLE_FAILURE("LLVMVectorType");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = simd_trunc_sat(comp_ctx, func_ctx,
|
|
is_signed ? "llvm.fptosi.sat.v2i32.v2f64"
|
|
: "llvm.fptoui.sat.v2i32.v2f64",
|
|
V128_f64x2_TYPE, out_vector_type))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(zero = LLVMConstNull(out_vector_type))) {
|
|
HANDLE_FAILURE("LLVMConstNull");
|
|
return false;
|
|
}
|
|
|
|
/* v2i32 -> v4i32 */
|
|
if (!(mask = LLVMConstVector(lanes, 4))) {
|
|
HANDLE_FAILURE("LLVMConstVector");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, result, zero, mask,
|
|
"extend"))) {
|
|
HANDLE_FAILURE("LLVMBuildShuffleVector");
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
static LLVMValueRef
|
|
simd_integer_convert(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
bool is_signed, LLVMValueRef vector,
|
|
LLVMTypeRef out_vector_type)
|
|
|
|
{
|
|
LLVMValueRef result;
|
|
result = is_signed ? LLVMBuildSIToFP(comp_ctx->builder, vector,
|
|
out_vector_type, "converted")
|
|
: LLVMBuildUIToFP(comp_ctx->builder, vector,
|
|
out_vector_type, "converted");
|
|
if (!result) {
|
|
HANDLE_FAILURE("LLVMBuildSIToFP/LLVMBuildUIToFP");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_f32x4_convert_i32x4(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_signed)
|
|
{
|
|
LLVMValueRef vector, result;
|
|
|
|
if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
V128_i32x4_TYPE, "vec"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(result = simd_integer_convert(comp_ctx, func_ctx, is_signed, vector,
|
|
V128_f32x4_TYPE))) {
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_f64x2_convert_i32x4(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool is_signed)
|
|
{
|
|
LLVMValueRef vector, mask, result;
|
|
LLVMValueRef lanes[] = {
|
|
LLVM_CONST(i32_zero),
|
|
LLVM_CONST(i32_one),
|
|
};
|
|
LLVMTypeRef out_vector_type;
|
|
|
|
if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
V128_i32x4_TYPE, "vec"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(out_vector_type = LLVMVectorType(F64_TYPE, 4))) {
|
|
HANDLE_FAILURE("LLVMVectorType");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = simd_integer_convert(comp_ctx, func_ctx, is_signed, vector,
|
|
out_vector_type))) {
|
|
return false;
|
|
}
|
|
|
|
/* v4f64 -> v2f64 */
|
|
if (!(mask = LLVMConstVector(lanes, 2))) {
|
|
HANDLE_FAILURE("LLVMConstVector");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, result, result,
|
|
mask, "trunc"))) {
|
|
HANDLE_FAILURE("LLVMBuildShuffleVector");
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
static bool
|
|
simd_extadd_pairwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
LLVMTypeRef in_vector_type, LLVMTypeRef out_vector_type,
|
|
bool is_signed)
|
|
{
|
|
LLVMValueRef vector, even_mask, odd_mask, sub_vector_even, sub_vector_odd,
|
|
result;
|
|
|
|
LLVMValueRef even_element[] = {
|
|
LLVM_CONST(i32_zero), LLVM_CONST(i32_two), LLVM_CONST(i32_four),
|
|
LLVM_CONST(i32_six), LLVM_CONST(i32_eight), LLVM_CONST(i32_ten),
|
|
LLVM_CONST(i32_twelve), LLVM_CONST(i32_fourteen),
|
|
};
|
|
|
|
LLVMValueRef odd_element[] = {
|
|
LLVM_CONST(i32_one), LLVM_CONST(i32_three),
|
|
LLVM_CONST(i32_five), LLVM_CONST(i32_seven),
|
|
LLVM_CONST(i32_nine), LLVM_CONST(i32_eleven),
|
|
LLVM_CONST(i32_thirteen), LLVM_CONST(i32_fifteen),
|
|
};
|
|
|
|
/* assumption about i16x8 from i8x16 and i32x4 from i16x8 */
|
|
uint8 mask_length = V128_i16x8_TYPE == out_vector_type ? 8 : 4;
|
|
|
|
if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, in_vector_type,
|
|
"vector"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(even_mask = LLVMConstVector(even_element, mask_length))
|
|
|| !(odd_mask = LLVMConstVector(odd_element, mask_length))) {
|
|
HANDLE_FAILURE("LLVMConstVector");
|
|
return false;
|
|
}
|
|
|
|
/* shuffle a <16xi8> vector to two <8xi8> vectors */
|
|
if (!(sub_vector_even = LLVMBuildShuffleVector(
|
|
comp_ctx->builder, vector, vector, even_mask, "pick_even"))
|
|
|| !(sub_vector_odd = LLVMBuildShuffleVector(
|
|
comp_ctx->builder, vector, vector, odd_mask, "pick_odd"))) {
|
|
HANDLE_FAILURE("LLVMBuildShuffleVector");
|
|
return false;
|
|
}
|
|
|
|
/* sext/zext <8xi8> to <8xi16> */
|
|
if (is_signed) {
|
|
if (!(sub_vector_even =
|
|
LLVMBuildSExt(comp_ctx->builder, sub_vector_even,
|
|
out_vector_type, "even_sext"))
|
|
|| !(sub_vector_odd =
|
|
LLVMBuildSExt(comp_ctx->builder, sub_vector_odd,
|
|
out_vector_type, "odd_sext"))) {
|
|
HANDLE_FAILURE("LLVMBuildSExt");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if (!(sub_vector_even =
|
|
LLVMBuildZExt(comp_ctx->builder, sub_vector_even,
|
|
out_vector_type, "even_zext"))
|
|
|| !(sub_vector_odd =
|
|
LLVMBuildZExt(comp_ctx->builder, sub_vector_odd,
|
|
out_vector_type, "odd_zext"))) {
|
|
HANDLE_FAILURE("LLVMBuildZExt");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!(result = LLVMBuildAdd(comp_ctx->builder, sub_vector_even,
|
|
sub_vector_odd, "sum"))) {
|
|
HANDLE_FAILURE("LLVMBuildAdd");
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i16x8_extadd_pairwise_i8x16(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
bool is_signed)
|
|
{
|
|
return simd_extadd_pairwise(comp_ctx, func_ctx, V128_i8x16_TYPE,
|
|
V128_i16x8_TYPE, is_signed);
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i32x4_extadd_pairwise_i16x8(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx,
|
|
bool is_signed)
|
|
{
|
|
return simd_extadd_pairwise(comp_ctx, func_ctx, V128_i16x8_TYPE,
|
|
V128_i32x4_TYPE, is_signed);
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i16x8_q15mulr_sat(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx)
|
|
{
|
|
LLVMValueRef lhs, rhs, pad, offset, min, max, result;
|
|
LLVMTypeRef vector_ext_type;
|
|
|
|
if (!(rhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i16x8_TYPE,
|
|
"rhs"))
|
|
|| !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
V128_i16x8_TYPE, "lhs"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(vector_ext_type = LLVMVectorType(I32_TYPE, 8))) {
|
|
HANDLE_FAILURE("LLVMVectorType");
|
|
return false;
|
|
}
|
|
|
|
if (!(lhs = LLVMBuildSExt(comp_ctx->builder, lhs, vector_ext_type,
|
|
"lhs_v8i32"))
|
|
|| !(rhs = LLVMBuildSExt(comp_ctx->builder, rhs, vector_ext_type,
|
|
"rhs_v8i32"))) {
|
|
HANDLE_FAILURE("LLVMBuildSExt");
|
|
return false;
|
|
}
|
|
|
|
/* 0x4000 and 15*/
|
|
if (!(pad = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE,
|
|
0x4000, 8))
|
|
|| !(offset = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE,
|
|
15, 8))) {
|
|
return false;
|
|
}
|
|
|
|
/* TODO: looking for x86 intrinsics about integer"fused multiply-and-add" */
|
|
/* S.SignedSaturate((x * y + 0x4000) >> 15) */
|
|
if (!(result = LLVMBuildMul(comp_ctx->builder, lhs, rhs, "mul"))) {
|
|
HANDLE_FAILURE("LLVMBuildMul");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildAdd(comp_ctx->builder, result, pad, "add"))) {
|
|
HANDLE_FAILURE("LLVMBuildAdd");
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildAShr(comp_ctx->builder, result, offset, "ashr"))) {
|
|
HANDLE_FAILURE("LLVMBuildAShr");
|
|
return false;
|
|
}
|
|
|
|
if (!(min = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE,
|
|
0xffff8000, 8))
|
|
|| !(max = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE,
|
|
0x00007fff, 8))) {
|
|
return false;
|
|
}
|
|
|
|
/* sat after trunc will let *sat* part be optimized */
|
|
if (!(result = simd_saturate(comp_ctx, func_ctx, e_sat_i32x8, result, min,
|
|
max, true))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildTrunc(comp_ctx->builder, result, V128_i16x8_TYPE,
|
|
"down_to_v8i16"))) {
|
|
HANDLE_FAILURE("LLVMBuildTrunc");
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
enum integer_extmul_type {
|
|
e_i16x8_extmul_i8x16,
|
|
e_i32x4_extmul_i16x8,
|
|
e_i64x2_extmul_i32x4,
|
|
};
|
|
|
|
static bool
|
|
simd_integer_extmul(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
|
bool lower_half, bool is_signed,
|
|
enum integer_extmul_type itype)
|
|
{
|
|
LLVMValueRef vec1, vec2, result;
|
|
enum integer_extend_type ext_type[] = {
|
|
e_ext_i8x16,
|
|
e_ext_i16x8,
|
|
e_ext_i32x4,
|
|
};
|
|
LLVMTypeRef in_vector_type[] = {
|
|
V128_i8x16_TYPE,
|
|
V128_i16x8_TYPE,
|
|
V128_i32x4_TYPE,
|
|
};
|
|
|
|
if (!(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type[itype], "vec1"))
|
|
|| !(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx,
|
|
in_vector_type[itype], "vec2"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(vec1 = simd_integer_extension(comp_ctx, func_ctx, ext_type[itype],
|
|
vec1, lower_half, is_signed))
|
|
|| !(vec2 = simd_integer_extension(comp_ctx, func_ctx, ext_type[itype],
|
|
vec2, lower_half, is_signed))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(result = LLVMBuildMul(comp_ctx->builder, vec1, vec2, "product"))) {
|
|
return false;
|
|
}
|
|
|
|
return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result");
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i16x8_extmul_i8x16(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
return simd_integer_extmul(comp_ctx, func_ctx, lower_half, is_signed,
|
|
e_i16x8_extmul_i8x16);
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i32x4_extmul_i16x8(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
return simd_integer_extmul(comp_ctx, func_ctx, lower_half, is_signed,
|
|
e_i32x4_extmul_i16x8);
|
|
}
|
|
|
|
bool
|
|
aot_compile_simd_i64x2_extmul_i32x4(AOTCompContext *comp_ctx,
|
|
AOTFuncContext *func_ctx, bool lower_half,
|
|
bool is_signed)
|
|
{
|
|
return simd_integer_extmul(comp_ctx, func_ctx, lower_half, is_signed,
|
|
e_i64x2_extmul_i32x4);
|
|
}
|