Skip to content

Commit

Permalink
dmd: more s390x va_arg implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
liushuyu committed Jan 19, 2025
1 parent 4c8495a commit c0cd3a2
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 5 deletions.
2 changes: 2 additions & 0 deletions dmd/argtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ namespace dmd
TypeTuple *toArgTypes_sysv_x64(Type *t);
// in argtypes_aarch64.d
TypeTuple *toArgTypes_aarch64(Type *t);
// in argtypes_s390x.d
TypeTuple *toArgTypes_s390x(Type *t);
bool isHFVA(Type *t, int maxNumElements = 4, Type **rewriteType = nullptr);
}
74 changes: 74 additions & 0 deletions dmd/argtypes_s390x.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Break down a D type into basic (register) types for the IBM Z ELF ABI.
*
* Copyright: Copyright (C) 2024-2025 by The D Language Foundation, All Rights Reserved
* Authors: Martin Kinkelin
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_s390x.d, _argtypes_s390x.d)
* Documentation: https://dlang.org/phobos/dmd_argtypes_s390x.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_s390x.d
*/

module dmd.argtypes_s390x;

import dmd.astenums;
import dmd.mtype;
import dmd.typesem;

/****************************************************
* This breaks a type down into 'simpler' types that can be passed to a function
* in registers, and returned in registers.
* This is the implementation for the IBM Z ELF ABI,
* based on https://github.com/IBM/s390x-abi/releases/download/v1.6/lzsabi_s390x.pdf.
* Params:
* t = type to break down
* Returns:
* tuple of types, each element can be passed in a register.
* A tuple of zero length means the type cannot be passed/returned in registers.
* null indicates a `void`.
*/
TypeTuple toArgTypes_s390x(Type t)
{
if (t == Type.terror)
return new TypeTuple(t);

const size = cast(size_t) t.size();
if (size == 0)
return null;

// TODO
// Implement the rest of the va args passing
//...
Type tb = t.toBasetype();
const isAggregate = tb.ty == Tstruct || tb.ty == Tsarray || tb.ty == Tarray || tb.ty == Tdelegate || tb.iscomplex();
if (!isAggregate)
return new TypeTuple(t);
// unwrap single-float struct per ABI requirements
if (auto tstruct = t.isTypeStruct())
{
if (tstruct.sym.fields.length == 1)
{
Type fieldType = tstruct.sym.fields[0].type.toBasetype();
if (fieldType.isfloating())
{
return new TypeTuple(fieldType);
}
}
}

// pass remaining aggregates in 1 or 2 GP registers
static Type getGPType(size_t size)
{
switch (size)
{
case 1: return Type.tint8;
case 2: return Type.tint16;
case 4: return Type.tint32;
case 8: return Type.tint64;
default:
import dmd.typesem : sarrayOf;
return Type.tint64.sarrayOf((size + 7) / 8);
}
}
return new TypeTuple(getGPType(size));
}
9 changes: 9 additions & 0 deletions dmd/cxxfrontend.d
Original file line number Diff line number Diff line change
Expand Up @@ -698,4 +698,13 @@ version (IN_LLVM)
import dmd.argtypes_x86;
return dmd.argtypes_x86.toArgTypes_x86(t);
}

/***********************************************************
* argtypes_s390x.d
*/
TypeTuple toArgTypes_s390x(Type t)
{
import dmd.argtypes_s390x;
return dmd.argtypes_s390x.toArgTypes_s390x(t);
}
}
2 changes: 2 additions & 0 deletions gen/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ TypeTuple *Target::toArgTypes(Type *t) {
return toArgTypes_sysv_x64(t);
if (arch == llvm::Triple::aarch64 || arch == llvm::Triple::aarch64_be)
return toArgTypes_aarch64(t);
if (arch == llvm::Triple::systemz)
return toArgTypes_s390x(t);
return nullptr;
}

Expand Down
51 changes: 46 additions & 5 deletions runtime/druntime/src/core/internal/vararg/s390x.d
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ T va_arg(T)(va_list ap)
{
static if (is(T U == __argTypes))
{
static if (U.length == 0 || U[0].sizeof > 8 || is(T1 == __vector))
static if (U.length == 0 || U[0].sizeof > 8 || is(U[0] == __vector))
{
// Always passed in memory (varying vectors are passed in parameter area)
auto p = *cast(T*) ap.__overflow_arg_area;
Expand All @@ -45,7 +45,7 @@ T va_arg(T)(va_list ap)
// Passed in $fr registers (FPR region starts at +0x80)
auto p = cast(T*) ap.__reg_save_area + 128 + ap.__fpr * 8;
ap.__fpr++;
return p;
return *p;
}
else
{
Expand All @@ -54,7 +54,7 @@ T va_arg(T)(va_list ap)
// no matter the actual size of the fp variable
// parameter slot is always 8-byte-wide (f32 is extended to f64)
ap.__overflow_arg_area += 8;
return p;
return *p;
}
}
else
Expand All @@ -65,7 +65,7 @@ T va_arg(T)(va_list ap)
// Passed in $gpr registers (GPR region starts at +0x10)
auto p = cast(T*) ap.__reg_save_area + 16 + ap.__gpr * 8;
ap.__gpr++;
return p;
return *p;
}
else
{
Expand All @@ -74,7 +74,7 @@ T va_arg(T)(va_list ap)
// no matter the actual size of the gpr variable
// parameter slot is always 8-byte-wide (after ABI adjustments)
ap.__overflow_arg_area += 8;
return p;
return *p;
}
}
}
Expand All @@ -93,6 +93,23 @@ T va_arg(T)(va_list ap)
void va_arg()(va_list ap, TypeInfo ti, void* parmn)
{
TypeInfo arg1, arg2;
if (TypeInfo_Struct ti_struct = cast(TypeInfo_Struct) ti)
{
// handle single-float element struct
const rtFields = ti_struct.offTi();
if (rtFields && rtFields.length == 1)
{
TypeInfo field1TypeInfo = rtFields[0].ti;
if (field1TypeInfo is typeid(float) || field1TypeInfo is typeid(double))
{
auto tsize = field1TypeInfo.tsize;
auto toffset = rtFields[0].offset;
parmn[0..tsize] = p[toffset..tsize];
return;
}
}
}

if (!ti.argTypes(arg1, arg2))
{
TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector) arg1 : null;
Expand All @@ -117,6 +134,30 @@ void va_arg()(va_list ap, TypeInfo ti, void* parmn)
parmn[0..tsize] = p[0..tsize];
}
}
else if (arg1 && (arg1 is typeid(float) || arg1 is typeid(double)))
{
// Maybe passed in $fr registers
if (ap.__fpr <= 4)
{
// Passed in $fr registers (FPR region starts at +0x80)
auto p = cast(T*) ap.__reg_save_area + 128 + ap.__fpr * 8;
ap.__fpr++;
parmn[0..tsize] = p[0..tsize];
}
else
{
// overflow arguments
auto p = cast(T*) ap.__overflow_arg_area;
// no matter the actual size of the fp variable
// parameter slot is always 8-byte-wide (f32 is extended to f64)
ap.__overflow_arg_area += 8;
parmn[0..tsize] = p[0..tsize];
}
}
else
{
assert(false, "unhandled va_arg type!");
}
assert(!arg2);
}
else
Expand Down

0 comments on commit c0cd3a2

Please sign in to comment.