blob: 656eb6dcbf24e5adc8ff6ca969da92a223686ca0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Alexander Astapchuk
*/
/**
* @file
* @brief CallSig class and related constants declaration.
*/
#if !defined(__CSIG_H_INCLUDED__)
#define __CSIG_H_INCLUDED__
#include "enc.h"
#include <vector>
using std::vector;
namespace Jitrino {
namespace Jet {
/**
* @defgroup JET_CCONV Calling conventions description.
* @{
*/
/**
* @brief Order of parameters is left-to-right.
*/
#define CCONV_L2R (0x00000001)
/**
* @brief Caller must restore stack (pop out arguments).
*/
#define CCONV_CALLER_POPS (0x00000002)
/**
* @brief When entering a function, obey the (sp)%%4 == 0 rule.
*/
#define CCONV_STACK_ALIGN4 (0x00000004)
/**
* @brief When entering a function, obey the (sp+8)%%16 == 0 rule (Intel 64
* convention).
*/
#define CCONV_STACK_ALIGN_HALF16 (0x00000008)
/**
* @brief When entering a function, obey the (sp)%%16 == 0 rule.
*/
#define CCONV_STACK_ALIGN16 (0x00000010)
/**
* @brief Mask to extract stack alignment form calling convention.
*/
#define CCONV_STACK_ALIGN_MASK (CCONV_STACK_ALIGN4 | CCONV_STACK_ALIGN_HALF16 | CCONV_STACK_ALIGN16)
/**
* @brief All args go though memory.
*/
#define CCONV_MEM (0x00000020)
/**
* @brief Use FPU register stack to return floating point, xmm0 otherwise.
*/
#define CCONV_RETURN_FP_THROUGH_FPU (0x00000040)
/**
* @brief IA-32's stdcall convention.
*/
#define CCONV_STDCALL_IA32 (CCONV_MEM | CCONV_RETURN_FP_THROUGH_FPU)
/**
* @brief IA-32's cdecl convention.
*/
#define CCONV_CDECL_IA32 (CCONV_CALLER_POPS | CCONV_MEM | CCONV_RETURN_FP_THROUGH_FPU)
#ifdef _EM64T_
/**
* @brief EM64T calling convention.
*/
#define CCONV_EM64T (CCONV_STACK_ALIGN_HALF16 | CCONV_CALLER_POPS)
/**
* @brief On IA-32 it's CCONV_CDECL_IA32, on EM64T it's CCONV_EM64T.
*/
#define CCONV_STDCALL CCONV_EM64T
/**
* @brief On IA-32 it's CCONV_CDECL_IA32, on EM64T it's CCONV_EM64T.
*/
#define CCONV_CDECL CCONV_EM64T
#define CCONV_PLATFORM CCONV_EM64T
#ifdef _WIN32
/// A nubmer of FR registers dedicated to pass float-point arguments.
#define MAX_FR_ARGS (4)
#else
#define MAX_FR_ARGS (8)
#endif
#else
#define CCONV_STDCALL CCONV_STDCALL_IA32
#define CCONV_CDECL CCONV_CDECL_IA32
#define CCONV_PLATFORM CCONV_CDECL
#define MAX_FR_ARGS (0)
#endif
/**
* @brief IA-32's DRLVM's convention for managed code.
*/
#define CCONV_MANAGED_IA32 (CCONV_L2R | CCONV_MEM | CCONV_STACK_ALIGN16)
/**
* @brief A special case - VM's helper MULTIANEWARRAY always has cdecl-like
* convention.
*/
#define CCONV_MULTIANEWARRAY CCONV_CDECL_IA32
#ifdef _EM64T_
/**
* @brief On IA-32 it's CCONV_MANAGED_IA32, on EM64T it's CCONV_EM64T.
*/
#define CCONV_MANAGED CCONV_EM64T
#else
#define CCONV_MANAGED CCONV_MANAGED_IA32
#endif
#define CCONV_HELPERS CCONV_STDCALL
///@} // ~JET_CCONV
/**
* @brief Describes method's signature - number of arguments, their types,
* calling convention, etc.
*
* A CallSig object which describes a function with stdcall calling
* convention which has 2 argument - an integer and float:
*
* @code
* CallSig ssig(CCONV_STDCALL, i32, flt32)
* @endcode
*
* Arguments are always numbered and referred to in left-to-right order:
* @code
* foo(arg 0, arg 1, arg 2)
CallSig csig(cc, arg0 type, arg1 type, arg2 type);
csig.get(2) refers to arg 2.
* @endcode
*
* An offsets of arguments are calculated relative to #sp with a presumption
* that stack frame for argument passing is prepared fist. I.e. the expected
* call preparation sequence would be
* @code
* CallSig csig();
* sub sp, csig.size()
* for i=0; i<csig.count(); i++
* mov [sp+csig.off(i)], arg#i
* @endcode
*
* Until #CCONV_L2R specified, the offsets are calculated as if the arguments
* were 'push-ed' to the stack from right-to-left.
*
* In other words, by default the 0th argument is on the top of the stack.
*
* With #CCONV_L2R, the least argument is on the top of the stack.
*
* The current implementation implies the presumption that a set of
* calle-save registers is the same across all calling conventions.
*/
class CallSig {
public:
/**
* @brief No-op.
*
* @note This ctor leaves CallSig object in unpredictable state.
* The #init() method \b must be called before any usage.
*/
CallSig(void)
{
m_cc = (unsigned)~0;
}
/**
* @brief Initializes CallSig object with the given arg types.
*/
CallSig(unsigned cc, jtype ret = jvoid,
jtype arg0=jvoid, jtype arg1=jvoid, jtype arg2=jvoid,
jtype arg3=jvoid, jtype arg4=jvoid, jtype arg5=jvoid)
{
m_cc = cc;
m_ret_jt = ret;
if (arg0 != jvoid) { m_args.push_back(arg0); }
if (arg1 != jvoid) { m_args.push_back(arg1); }
if (arg2 != jvoid) { m_args.push_back(arg2); }
if (arg3 != jvoid) { m_args.push_back(arg3); }
if (arg4 != jvoid) { m_args.push_back(arg4); }
if (arg5 != jvoid) { m_args.push_back(arg5); }
// No jvoid type argument can be presented.
assert(find(m_args.begin(), m_args.end(), jvoid) == m_args.end());
init();
}
#if 0
/**
* Seems there is no need in such ctor. All the existing usage
* cases are covered by CallSig(cc, jtype*N = jvoid)
*/
CallSig(unsigned cc, unsigned num, ...)
{
va_list valist;
va_start(valist, num);
m_args.resize(num);
assert(sizeof(jtype)==sizeof(int));
for (unsigned i=0; i<num; i++) {
jtype jt = (jtype)va_arg(valist, int);
assert(i8<=jt && jt<num_jtypes);
m_args[i] = jt;
}
m_cc = cc;
init();
}
#endif
/**
* @brief Constructs and initializes CallSig object with the given
* calling convention and list of args types.
*/
CallSig(unsigned cc, const jtype ret, const vector<jtype>& args)
{
m_ret_jt = ret;
init(cc, args);
}
/**
* @brief Initializes CallSig object with the given calling convention
* and list of args types.
*/
void init(unsigned cc, const vector<jtype>& args)
{
m_args = args;
m_cc = cc;
init();
}
/**
* @brief Returns used calling convention.
*/
unsigned cc(void) const
{
return m_cc;
}
/**
* @brief Returns true if caller must restore the stack.
* @see CCONV_CALLER_POPS
* @see CCONV_CDECL
*/
bool caller_pops(void) const
{
return (m_cc & CCONV_CALLER_POPS);
}
/**
* @brief Returns size (in bytes) of padding area to achieve proper alignment.
*/
unsigned alignment() const {
return m_alignment;
}
/**
* @brief Returns size (in bytes) of the stack size needed to pass args
* that go through the stack.
*/
unsigned size(void) const
{
return m_stack;
}
/**
* @brief Returns number of arguments.
*/
unsigned count(void) const
{
assert(m_data.size() == m_args.size());
return (unsigned) m_args.size();
}
/**
* @brief Returns appropriate AR to pass the given argument, or ar_x
* if the argument comes through the stack.
*/
AR reg(unsigned i) const
{
assert(i<count());
return m_data[i]<=0 ? ar_x : (AR)m_data[i];
}
/**
* @params i slot number. For example on IA32 ret_reg(0) is eax, ret_reg(1) is edx.
* @returns register which holds return value, or ar_x
* of the value comes through the memory.
*/
AR ret_reg(unsigned i) const
{
assert(i < 2);
return (m_ret_reg[i] <= 0) ? ar_x : (AR)m_ret_reg[i];
}
/**
* @returns type of return value.
*/
jtype ret_jt() const { return m_ret_jt; }
/**
* @returns Offset (in bytes) from #sp of the given argument, or -1 if
* the argument is passed on register.
*/
int off(unsigned i) const
{
assert(i<count());
return m_data[i]<=0 ? -m_data[i] : -1;
}
/**
* @returns An Opnd to refer the given method's argument.
*
* If the argument is passed through the stack, then memory operand
* with base()==sp and appropriate displacement returned.
*
* If the \c offset parameter is not 0, then it's added to the argument's
* displacement.
*
* If the argument is passed through a register, then register operand
* returned, and \c offset parameter is ignored.
*
* The returned Opnd object has proper type. For \link #is_big big type
* \endlink the \link #jtmov appropriate supported type \endlink
* returned.
*/
Opnd get(unsigned i, int offset = 0) const
{
AR ar = reg(i);
jtype jtm = jtmov(jt(i));
if (ar != ar_x) {
return Opnd(jtm, ar);
}
return Opnd(jtm, sp, off(i) + offset);
}
/**
* @brief Returns type of the argument.
*/
jtype jt(unsigned i) const
{
assert(i<count());
return m_args[i];
}
/**
* @brief Tests whether given AR is used to pass an argument for the
* this CallSig.
*/
bool uses(AR ar) const
{
if (ar != ar_x) {
for (unsigned i=0; i<count(); i++) {
if (reg(i) == ar) {
return true;
}
}
}
return false;
}
private:
/**
* @brief Initializes CallSig object.
*
* Counts args offsets and required stack size.
*/
void init();
/**
* @brief An info about argument types.
*/
::std::vector<jtype> m_args;
/**
* @brief An info about argument offsets (m_data[i]<0) or registers
* (m_data[i]>0 => (AR)m_data[i]).
*
* Though offsets are stored as negative values, they are positive
* offsets to #sp. The sign here is used as additional flag whether the
* value must be interpreted as offset (sig<=0) or as a register
* (sign>0).
*
* This implies presumption that a valid AR is alwys > 0.
*/
::std::vector<int> m_data;
/**
* @brief Returns size (in bytes) of padding area to achieve proper alignment.
*/
unsigned m_alignment;
/**
* @brief Size (in bytes) of stack frame needed to pass arguments.
*
* ... with all the alignment properties taken into account.
*/
unsigned m_stack;
/**
* @brief \link JET_CCONV calling convention \endlink for this
* CallSig object.
*/
unsigned m_cc;
/**
* @brief
*/
int m_ret_reg[2];
jtype m_ret_jt;
};
/**
* @brief CallSig for stdcall function that takes no args.
*/
extern const CallSig helper_v;
extern const CallSig platform_v;
}}; // ~namespace Jitrino::Jet
#endif // ~__CSIG_H_INCLUDED__