Some compilers won't build ARM due to how guest ABI
has been implemented.
The error is: "left shift count >= width of type"
[-Werror=shift-count-overflow]
The error is triggered when there is a left shift > the variable size
(in bits); this leads to undefined behaviour.
This is a compile time vs run time problem; the code is technically
fine, but the compiler is not able to understand this.
For example in aapcs64:
struct Argument<Aapcs64, Integer, typename std::enable_if<
std::is_integral<Integer>::value>::type> : public Aapcs64ArgumentBase
{
[...]
if (sizeof(Integer) == 16 && state.ngrn + 1 <= state.MAX_GRN) {
Integer low = tc->readIntReg(state.ngrn++);
Integer high = tc->readIntReg(state.ngrn++);
high = high << 64;
return high | low;
}
}
Even if the sizeof operator will be evaluated at compile time,
the block will be executed at runtime: the block will still be part of
the code if Integer = uint32_t.
The compiler will then throw an error because we are left shifting an
uint32_t by 64 bits.
Error arising on:
Compiler: gcc/5.4.0
Distro: Ubuntu 16.04 LTS
Change-Id: Iaafe030b7262c5fb162afe7118ae592a1a759a58
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/26990
Reviewed-by: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
443 lines
14 KiB
C++
443 lines
14 KiB
C++
/*
|
|
* Copyright 2019 Google Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef __ARCH_ARM_AAPCS64_HH__
|
|
#define __ARCH_ARM_AAPCS64_HH__
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "arch/arm/intregs.hh"
|
|
#include "arch/arm/utility.hh"
|
|
#include "base/intmath.hh"
|
|
#include "cpu/thread_context.hh"
|
|
#include "sim/guest_abi.hh"
|
|
#include "sim/syscall_emul_buf.hh"
|
|
|
|
class ThreadContext;
|
|
|
|
struct Aapcs64
|
|
{
|
|
struct State
|
|
{
|
|
int ngrn=0; // Next general purpose register number.
|
|
int nsrn=0; // Next SIMD and floating point register number.
|
|
Addr nsaa; // Next stacked argument address.
|
|
|
|
// The maximum allowed general purpose register number.
|
|
static const int MAX_GRN = 7;
|
|
// The maximum allowed SIMD and floating point register number.
|
|
static const int MAX_SRN = 7;
|
|
|
|
explicit State(const ThreadContext *tc) :
|
|
nsaa(tc->readIntReg(ArmISA::INTREG_SPX))
|
|
{}
|
|
};
|
|
};
|
|
|
|
namespace GuestABI
|
|
{
|
|
|
|
/*
|
|
* Short Vectors
|
|
*/
|
|
|
|
// A short vector is a machine type that is composed of repeated instances of
|
|
// one fundamental integral or floating- point type. It may be 8 or 16 bytes
|
|
// in total size. We represent it here as an opaque blob of data with an
|
|
// appropriate alignment requirement.
|
|
|
|
template <typename T, std::size_t count, typename Enabled=void>
|
|
using Aapcs64ShortVectorCandidate =
|
|
alignas(sizeof(T) * count) uint8_t [sizeof(T) * count];
|
|
|
|
template <typename T, std::size_t count>
|
|
using Aapcs64ShortVector = Aapcs64ShortVectorCandidate<T, count,
|
|
typename std::enable_if<
|
|
(std::is_integral<T>::value || std::is_floating_point<T>::value) &&
|
|
(sizeof(T) * count == 8 || sizeof(T) * count == 16)>::type>;
|
|
|
|
template <typename T>
|
|
struct IsAapcs64ShortVector : public std::false_type {};
|
|
|
|
template <typename E, size_t N>
|
|
struct IsAapcs64ShortVector<Aapcs64ShortVector<E, N>> : public std::true_type
|
|
{};
|
|
|
|
/*
|
|
* Composite Types
|
|
*/
|
|
|
|
template <typename T, typename Enabled=void>
|
|
struct IsAapcs64Composite : public std::false_type {};
|
|
|
|
template <typename T>
|
|
struct IsAapcs64Composite<T, typename std::enable_if<
|
|
(std::is_array<T>::value ||
|
|
std::is_class<T>::value ||
|
|
std::is_union<T>::value) &&
|
|
// VarArgs is technically a composite type, but it's not a normal argument.
|
|
!IsVarArgs<T>::value &&
|
|
// Short vectors are also composite types, but don't treat them as one.
|
|
!IsAapcs64ShortVector<T>::value
|
|
>::type> : public std::true_type
|
|
{};
|
|
|
|
// Homogeneous Aggregates
|
|
// These *should* be any aggregate type which has only one type of member, but
|
|
// we can't actually detect that or manipulate that with templates. Instead,
|
|
// we approximate that by detecting only arrays with that property.
|
|
|
|
template <typename T, std::size_t count, typename Enabled=void>
|
|
using Aapcs64HomogeneousAggregate = T[count];
|
|
|
|
// An Homogeneous Floating-Point Aggregate (HFA) is an Homogeneous Aggregate
|
|
// with a Fundemental Data Type that is a Floating-Point type and at most four
|
|
// uniquely addressable members.
|
|
|
|
template <typename T, std::size_t count>
|
|
using Aapcs64Hfa = Aapcs64HomogeneousAggregate<T, count,
|
|
typename std::enable_if<std::is_floating_point<T>::value &&
|
|
count <= 4>::type>;
|
|
|
|
template <typename T, typename Enabled=void>
|
|
struct IsAapcs64Hfa : public std::false_type {};
|
|
|
|
template <typename E, size_t N>
|
|
struct IsAapcs64Hfa<E[N], Aapcs64Hfa<E, N>> : public std::true_type {};
|
|
|
|
// An Homogeneous Short-Vector Aggregate (HVA) is an Homogeneous Aggregate with
|
|
// a Fundamental Data Type that is a Short-Vector type and at most four
|
|
// uniquely addressable members.
|
|
|
|
template <typename T, std::size_t count>
|
|
using Aapcs64Hva = Aapcs64HomogeneousAggregate<T, count,
|
|
typename std::enable_if<IsAapcs64ShortVector<T>::value &&
|
|
count <= 4>::type>;
|
|
|
|
template <typename T, typename Enabled=void>
|
|
struct IsAapcs64Hva : public std::false_type {};
|
|
|
|
template <typename E, size_t N>
|
|
struct IsAapcs64Hva<E[N], Aapcs64Hva<E, N>> : public std::true_type {};
|
|
|
|
// A shorthand to test if a type is an HVA or an HFA.
|
|
template <typename T, typename Enabled=void>
|
|
struct IsAapcs64Hxa : public std::false_type {};
|
|
|
|
template <typename T>
|
|
struct IsAapcs64Hxa<T, typename std::enable_if<
|
|
IsAapcs64Hfa<T>::value && IsAapcs64Hva<T>::value>::type> :
|
|
public std::true_type
|
|
{};
|
|
|
|
struct Aapcs64ArgumentBase
|
|
{
|
|
template <typename T>
|
|
static T
|
|
loadFromStack(ThreadContext *tc, Aapcs64::State &state)
|
|
{
|
|
// The alignment is the larger of 8 or the natural alignment of T.
|
|
size_t align = std::max<size_t>(8, alignof(T));
|
|
// Increase the size to the next multiple of 8.
|
|
size_t size = roundUp(sizeof(T), 8);
|
|
|
|
// Align the stack.
|
|
state.nsaa = roundUp(state.nsaa, align);
|
|
|
|
// Extract the value from it.
|
|
TypedBufferArg<T> val(state.nsaa);
|
|
val.copyIn(tc->getVirtProxy());
|
|
|
|
// Move the nsaa past this argument.
|
|
state.nsaa += size;
|
|
|
|
// Return the value we extracted.
|
|
return gtoh(*val, ArmISA::byteOrder(tc));
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Floating point and Short-Vector arguments and return values.
|
|
*/
|
|
|
|
template <typename Float>
|
|
struct Argument<Aapcs64, Float, typename std::enable_if<
|
|
std::is_floating_point<Float>::value ||
|
|
IsAapcs64ShortVector<Float>::value>::type> :
|
|
public Aapcs64ArgumentBase
|
|
{
|
|
static Float
|
|
get(ThreadContext *tc, Aapcs64::State &state)
|
|
{
|
|
if (state.nsrn <= state.MAX_SRN) {
|
|
RegId id(VecRegClass, state.nsrn++);
|
|
return tc->readVecReg(id).laneView<Float, 0>();
|
|
}
|
|
|
|
return loadFromStack<Float>(tc, state);
|
|
}
|
|
};
|
|
|
|
template <typename Float>
|
|
struct Result<Aapcs64, Float, typename std::enable_if<
|
|
std::is_floating_point<Float>::value ||
|
|
IsAapcs64ShortVector<Float>::value>::type>
|
|
{
|
|
static void
|
|
store(ThreadContext *tc, const Float &f)
|
|
{
|
|
RegId id(VecRegClass, 0);
|
|
auto reg = tc->readVecReg(id);
|
|
reg.laneView<Float, 0>() = f;
|
|
tc->setVecReg(id, reg);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Integer arguments and return values.
|
|
*/
|
|
|
|
// This will pick up Addr as well, which should be used for guest pointers.
|
|
template <typename Integer>
|
|
struct Argument<Aapcs64, Integer, typename std::enable_if<
|
|
std::is_integral<Integer>::value && (sizeof(Integer) <= 8)
|
|
>::type> : public Aapcs64ArgumentBase
|
|
{
|
|
static Integer
|
|
get(ThreadContext *tc, Aapcs64::State &state)
|
|
{
|
|
if (state.ngrn <= state.MAX_GRN)
|
|
return tc->readIntReg(state.ngrn++);
|
|
|
|
// Max out ngrn since we've effectively saturated it.
|
|
state.ngrn = state.MAX_GRN + 1;
|
|
|
|
return loadFromStack<Integer>(tc, state);
|
|
}
|
|
};
|
|
|
|
template <typename Integer>
|
|
struct Argument<Aapcs64, Integer, typename std::enable_if<
|
|
std::is_integral<Integer>::value && (sizeof(Integer) > 8)
|
|
>::type> : public Aapcs64ArgumentBase
|
|
{
|
|
static Integer
|
|
get(ThreadContext *tc, Aapcs64::State &state)
|
|
{
|
|
if (alignof(Integer) == 16 && (state.ngrn % 2))
|
|
state.ngrn++;
|
|
|
|
if (sizeof(Integer) == 16 && state.ngrn + 1 <= state.MAX_GRN) {
|
|
Integer low = tc->readIntReg(state.ngrn++);
|
|
Integer high = tc->readIntReg(state.ngrn++);
|
|
high = high << 64;
|
|
return high | low;
|
|
}
|
|
|
|
// Max out ngrn since we've effectively saturated it.
|
|
state.ngrn = state.MAX_GRN + 1;
|
|
|
|
return loadFromStack<Integer>(tc, state);
|
|
}
|
|
};
|
|
|
|
template <typename Integer>
|
|
struct Result<Aapcs64, Integer, typename std::enable_if<
|
|
std::is_integral<Integer>::value && (sizeof(Integer) <= 8)
|
|
>::type>
|
|
{
|
|
static void
|
|
store(ThreadContext *tc, const Integer &i)
|
|
{
|
|
tc->setIntReg(0, i);
|
|
}
|
|
};
|
|
|
|
template <typename Integer>
|
|
struct Result<Aapcs64, Integer, typename std::enable_if<
|
|
std::is_integral<Integer>::value && (sizeof(Integer) > 8)
|
|
>::type>
|
|
{
|
|
static void
|
|
store(ThreadContext *tc, const Integer &i)
|
|
{
|
|
tc->setIntReg(0, (uint64_t)i);
|
|
tc->setIntReg(1, (uint64_t)(i >> 64));
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Homogeneous Floating-Point and Short-Vector Aggregates (HFAs and HVAs)
|
|
* argument and return values.
|
|
*/
|
|
|
|
template <typename T>
|
|
struct Aapcs64ArrayType { using Type = void; };
|
|
|
|
template <typename E, size_t N>
|
|
struct Aapcs64ArrayType<E[N]> { using Type = E; };
|
|
|
|
template <typename HA>
|
|
struct Argument<Aapcs64, HA, typename std::enable_if<
|
|
IsAapcs64Hxa<HA>::value>::type> : public Aapcs64ArgumentBase
|
|
{
|
|
static HA
|
|
get(ThreadContext *tc, Aapcs64::State &state)
|
|
{
|
|
using Elem = typename Aapcs64ArrayType<HA>::Type;
|
|
constexpr size_t Count = sizeof(HA) / sizeof(Elem);
|
|
|
|
if (state.nsrn + Count - 1 <= state.MAX_SRN) {
|
|
HA ha;
|
|
for (int i = 0; i < Count; i++)
|
|
ha[i] = Argument<Aapcs64, Elem>::get(tc, state);
|
|
return ha;
|
|
}
|
|
|
|
// Max out the nsrn since we effectively exhausted it.
|
|
state.nsrn = state.MAX_SRN + 1;
|
|
|
|
return loadFromStack<HA>(tc, state);
|
|
}
|
|
};
|
|
|
|
template <typename HA>
|
|
struct Result<Aapcs64, HA,
|
|
typename std::enable_if<IsAapcs64Hxa<HA>::value>::type>
|
|
{
|
|
static HA
|
|
store(ThreadContext *tc, const HA &ha)
|
|
{
|
|
using Elem = typename Aapcs64ArrayType<HA>::Type;
|
|
constexpr size_t Count = sizeof(HA) / sizeof(Elem);
|
|
|
|
for (int i = 0; i < Count; i++)
|
|
Result<Aapcs64, Elem>::store(tc, ha[i]);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Composite arguments and return values which are not HVAs or HFAs.
|
|
*/
|
|
|
|
template <typename Composite>
|
|
struct Argument<Aapcs64, Composite, typename std::enable_if<
|
|
IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value
|
|
>::type> : public Aapcs64ArgumentBase
|
|
{
|
|
static Composite
|
|
get(ThreadContext *tc, Aapcs64::State &state)
|
|
{
|
|
if (sizeof(Composite) > 16) {
|
|
// Composite values larger than 16 which aren't HFAs or HVAs are
|
|
// kept in a buffer, and the argument is actually a pointer to that
|
|
// buffer.
|
|
Addr addr = Argument<Aapcs64, Addr>::get(tc, state);
|
|
TypedBufferArg<Composite> composite(addr);
|
|
composite.copyIn(tc->getVirtProxy());
|
|
return gtoh(*composite, ArmISA::byteOrder(tc));
|
|
}
|
|
|
|
// The size of Composite must be 16 bytes or less after this point.
|
|
|
|
size_t bytes = sizeof(Composite);
|
|
using Chunk = uint64_t;
|
|
|
|
const int chunk_size = sizeof(Chunk);
|
|
const int regs = (bytes + chunk_size - 1) / chunk_size;
|
|
|
|
// Can it fit in GPRs?
|
|
if (state.ngrn + regs - 1 <= state.MAX_GRN) {
|
|
alignas(alignof(Composite)) uint8_t buf[bytes];
|
|
for (int i = 0; i < regs; i++) {
|
|
Chunk val = tc->readIntReg(state.ngrn++);
|
|
val = htog(val, ArmISA::byteOrder(tc));
|
|
size_t to_copy = std::min<size_t>(bytes, chunk_size);
|
|
memcpy(buf + i * chunk_size, &val, to_copy);
|
|
bytes -= to_copy;
|
|
}
|
|
return gtoh(*(Composite *)buf, ArmISA::byteOrder(tc));
|
|
}
|
|
|
|
// Max out the ngrn since we effectively exhausted it.
|
|
state.ngrn = state.MAX_GRN;
|
|
|
|
return loadFromStack<Composite>(tc, state);
|
|
}
|
|
};
|
|
|
|
template <typename Composite>
|
|
struct Result<Aapcs64, Composite, typename std::enable_if<
|
|
IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value
|
|
>::type>
|
|
{
|
|
static void
|
|
store(ThreadContext *tc, const Composite &c)
|
|
{
|
|
if (sizeof(Composite) > 16) {
|
|
Addr addr = tc->readIntReg(ArmISA::INTREG_X8);
|
|
TypedBufferArg<Composite> composite(addr);
|
|
*composite = htog(c, ArmISA::byteOrder(tc));
|
|
return;
|
|
}
|
|
|
|
// The size of Composite must be 16 bytes or less after this point.
|
|
|
|
size_t bytes = sizeof(Composite);
|
|
using Chunk = uint64_t;
|
|
|
|
const int chunk_size = sizeof(Chunk);
|
|
const int regs = (bytes + chunk_size - 1) / chunk_size;
|
|
|
|
Composite cp = htog(c, ArmISA::byteOrder(tc));
|
|
uint8_t *buf = (uint8_t *)&cp;
|
|
for (int i = 0; i < regs; i++) {
|
|
size_t to_copy = std::min<size_t>(bytes, chunk_size);
|
|
|
|
Chunk val;
|
|
memcpy(&val, buf, to_copy);
|
|
val = gtoh(val, ArmISA::byteOrder(tc));
|
|
|
|
tc->setIntReg(i, val);
|
|
|
|
bytes -= to_copy;
|
|
buf += to_copy;
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace GuestABI
|
|
|
|
#endif // __ARCH_ARM_AAPCS64_HH__
|