Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
aa53732
Alternate approach for HELPER_METHOD_FRAME removal for arithmetic div…
davidwrighton Mar 8, 2025
e2273af
Update alignment to be a bit better for the assembly routines
davidwrighton Mar 8, 2025
29364c3
Try to adjust jithelpers.cpp for better building on arm32
davidwrighton Mar 8, 2025
f7f6e3d
Fix more oops in the build
davidwrighton Mar 8, 2025
52e28a9
More attempts to fix build breaks... maybe I should actually set up a…
davidwrighton Mar 8, 2025
6796ab4
Get the types right for JIT_UDiv and JIT_UMod
davidwrighton Mar 8, 2025
7ed11fe
Swap the args around for the 64bit long/ulong divide helpers back to …
davidwrighton Mar 10, 2025
e76b3aa
Attempt to fix 32bit linux builds, and remove all use of FCThrow, and…
davidwrighton Mar 18, 2025
a5af1b4
Fix BCL build
davidwrighton Mar 18, 2025
19ec62a
Merge branch 'main' into alternate_approach_to_removingHMF_for_arithm…
davidwrighton Mar 18, 2025
3799b0d
Get this approach building on Linux X86 and Linux Arm ... turns out t…
davidwrighton Mar 19, 2025
f418940
Merge branch 'main' into alternate_approach_to_removingHMF_for_arithm…
davidwrighton Mar 21, 2025
8d6ba7e
Integrate am11's work into my branch
davidwrighton Mar 24, 2025
ee49f1c
Merge branch 'main' of https://github.com/dotnet/runtime into alterna…
davidwrighton Mar 24, 2025
a87b046
Fix x86 eh stuff
davidwrighton Mar 31, 2025
8cc6d52
Merge branch 'main' of https://github.com/dotnet/runtime into alterna…
davidwrighton Mar 31, 2025
37ae63f
Move the math helpers to be consistently FCalls
davidwrighton Mar 31, 2025
0722c32
Tweak macros
davidwrighton Mar 31, 2025
51db312
Merge branch 'main' of https://github.com/dotnet/runtime into alterna…
davidwrighton Apr 1, 2025
356f9ba
Code revewi from am11
davidwrighton Apr 1, 2025
eda6d06
Remove unused System namespace import
davidwrighton Apr 2, 2025
0eb3870
Merge branch 'main' of https://github.com/dotnet/runtime into alterna…
davidwrighton Apr 2, 2025
a21f51d
AAGGHH
davidwrighton Apr 4, 2025
5b7bfb6
Merge branch 'main' of github.com:dotnet/runtime into alternate_appro…
davidwrighton Apr 4, 2025
b3188c5
Code review comments
davidwrighton Apr 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: undo

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -79,5 +80,21 @@ internal static void ThrowClassAccessException(
{
ThrowClassAccessExceptionInternal(caller, callee);
}

// DivideByZero exception for being tail-called with 64bit int parameters (which have an effect on ABI on 32bit platforms)
[DoesNotReturn]
[DebuggerHidden]
internal static long ThrowDivideByZeroExceptionLong(long divisor, long dividend)
{
throw new DivideByZeroException();
}

// OverflowException for being tail-called with 64bit int parameters (which have an effect on ABI on 32bit platforms)
[DoesNotReturn]
[DebuggerHidden]
internal static long ThrowOverflowExceptionLong(long divisor, long dividend)
{
throw new OverflowException();
}
}
}
30 changes: 22 additions & 8 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,34 @@
#define DYNAMICJITHELPER_NOINDIRECT(code,fn,binderId) DYNAMICJITHELPER(code,fn,binderId)
#endif

#if defined(TARGET_32BIT) && defined (TARGET_ARM)
#define FEATURE_USE_HELPERS_FOR_32BIT_INT_DIV
#endif

// pfnHelper is set to NULL if it is a stubbed helper.
// It will be set in InitJITHelpers2

JITHELPER(CORINFO_HELP_UNDEF, NULL, METHOD__NIL)

// Arithmetic
#ifdef FEATURE_USE_HELPERS_FOR_32BIT_INT_DIV
JITHELPER(CORINFO_HELP_DIV, JIT_Div, METHOD__NIL)
JITHELPER(CORINFO_HELP_MOD, JIT_Mod, METHOD__NIL)
JITHELPER(CORINFO_HELP_UDIV, JIT_UDiv, METHOD__NIL)
JITHELPER(CORINFO_HELP_UMOD, JIT_UMod, METHOD__NIL)
#else
JITHELPER(CORINFO_HELP_DIV, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_MOD, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_UDIV, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_UMOD, NULL, METHOD__NIL)
#endif

// CORINFO_HELP_DBL2INT, CORINFO_HELP_DBL2UINT, and CORINFO_HELP_DBL2LONG get
// patched for CPUs that support SSE2 (P4 and above).
#ifndef TARGET_64BIT
#ifdef TARGET_32BIT
JITHELPER(CORINFO_HELP_LLSH, JIT_LLsh, METHOD__NIL)
JITHELPER(CORINFO_HELP_LRSH, JIT_LRsh, METHOD__NIL)
JITHELPER(CORINFO_HELP_LRSZ, JIT_LRsz, METHOD__NIL)
#else // !TARGET_64BIT
#else // TARGET_32BIT

JITHELPER(CORINFO_HELP_LLSH, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_LRSH, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_LRSZ, NULL, METHOD__NIL)
Expand All @@ -55,14 +65,18 @@
#ifndef TARGET_64BIT
DYNAMICJITHELPER(CORINFO_HELP_LMUL_OVF, NULL, METHOD__MATH__MULTIPLY_CHECKED_INT64)
DYNAMICJITHELPER(CORINFO_HELP_ULMUL_OVF, NULL, METHOD__MATH__MULTIPLY_CHECKED_UINT64)
#else
DYNAMICJITHELPER(CORINFO_HELP_LMUL_OVF, NULL, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_ULMUL_OVF, NULL, METHOD__NIL)
#endif // TARGET_64BIT
JITHELPER(CORINFO_HELP_LDIV, JIT_LDiv, METHOD__NIL)
JITHELPER(CORINFO_HELP_LMOD, JIT_LMod, METHOD__NIL)
JITHELPER(CORINFO_HELP_ULDIV, JIT_ULDiv, METHOD__NIL)
JITHELPER(CORINFO_HELP_ULMOD, JIT_ULMod, METHOD__NIL)
#else
DYNAMICJITHELPER(CORINFO_HELP_LMUL_OVF, NULL, METHOD__NIL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If CORINFO_HELP_LMUL/ULMUL_OVF are unused on 64-bit, I would expect them to be JITHELPER.

If they are used, I would expect them to look like the 32-bit ones and be outside the #ifndef TARGET_64BIT ifdef.

DYNAMICJITHELPER(CORINFO_HELP_ULMUL_OVF, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_LDIV, NULL, METHOD__NIL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Padding for the METHOD__NIL column is off.

JITHELPER(CORINFO_HELP_LMOD, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_ULDIV, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_ULMOD, NULL, METHOD__NIL)
#endif // TARGET_64BIT
JITHELPER(CORINFO_HELP_LNG2DBL, JIT_Lng2Dbl, METHOD__NIL)
JITHELPER(CORINFO_HELP_ULNG2DBL, JIT_ULng2Dbl, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBL2INT, JIT_Dbl2Int, METHOD__NIL)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14168,8 +14168,8 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call)
vnpExc = fgValueNumberDivisionExceptions(GT_DIV, call->gtArgs.GetUserArgByIndex(0)->GetNode(),
call->gtArgs.GetUserArgByIndex(1)->GetNode());
break;
case CORINFO_HELP_MOD:
case CORINFO_HELP_LMOD:
case CORINFO_HELP_MOD:
vnpExc = fgValueNumberDivisionExceptions(GT_MOD, call->gtArgs.GetUserArgByIndex(0)->GetNode(),
call->gtArgs.GetUserArgByIndex(1)->GetNode());
break;
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,16 @@ extern "C" PCODE g_pGetNonGCStaticBase;
PCODE g_pGetNonGCStaticBase;
extern "C" PCODE g_pPollGC;
PCODE g_pPollGC;
#ifdef TARGET_32BIT
extern "C" PCODE g_pThrowOverflowException;
PCODE g_pThrowOverflowException;
extern "C" PCODE g_pThrowDivideByZeroException;
PCODE g_pThrowDivideByZeroException;
extern "C" PCODE g_pThrowOverflowExceptionLong;
PCODE g_pThrowOverflowExceptionLong;
extern "C" PCODE g_pThrowDivideByZeroExceptionLong;
PCODE g_pThrowDivideByZeroExceptionLong;
#endif // TARGET_32BIT

void SystemDomain::LoadBaseSystemClasses()
{
Expand Down Expand Up @@ -1146,6 +1156,12 @@ void SystemDomain::LoadBaseSystemClasses()
g_pGetGCStaticBase = CoreLibBinder::GetMethod(METHOD__STATICSHELPERS__GET_GC_STATIC)->GetMultiCallableAddrOfCode();
g_pGetNonGCStaticBase = CoreLibBinder::GetMethod(METHOD__STATICSHELPERS__GET_NONGC_STATIC)->GetMultiCallableAddrOfCode();
g_pPollGC = CoreLibBinder::GetMethod(METHOD__THREAD__POLLGC)->GetMultiCallableAddrOfCode();
#ifdef TARGET_32BIT
g_pThrowOverflowException = CoreLibBinder::GetMethod(METHOD__THROWHELPERS__THROWOVERFLOWEXCEPTION)->GetMultiCallableAddrOfCode();
g_pThrowDivideByZeroException = CoreLibBinder::GetMethod(METHOD__THROWHELPERS__THROWDIVIDEBYZEROEXCEPTION)->GetMultiCallableAddrOfCode();
g_pThrowOverflowExceptionLong = CoreLibBinder::GetMethod(METHOD__THROWHELPERS__THROWOVERFLOWEXCEPTIONLONG)->GetMultiCallableAddrOfCode();
g_pThrowDivideByZeroExceptionLong = CoreLibBinder::GetMethod(METHOD__THROWHELPERS__THROWDIVIDEBYZEROEXCEPTIONLONG)->GetMultiCallableAddrOfCode();
#endif // TARGET_32BIT

#ifdef PROFILING_SUPPORTED
// Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,8 @@ DEFINE_METHOD(THROWHELPERS, THROWPLATFORMNOTSUPPORTEDEXCEPTION, ThrowPlatfor
DEFINE_METHOD(THROWHELPERS, THROWTYPENOTSUPPORTED, ThrowTypeNotSupportedException, SM_RetVoid)
DEFINE_METHOD(THROWHELPERS, THROWOVERFLOWEXCEPTION, ThrowOverflowException, SM_RetVoid)
DEFINE_METHOD(THROWHELPERS, THROWDIVIDEBYZEROEXCEPTION, ThrowDivideByZeroException, SM_RetVoid)
DEFINE_METHOD(THROWHELPERS, THROWOVERFLOWEXCEPTIONLONG, ThrowOverflowExceptionLong, NoSig)
DEFINE_METHOD(THROWHELPERS, THROWDIVIDEBYZEROEXCEPTIONLONG, ThrowDivideByZeroExceptionLong, NoSig)
DEFINE_METHOD(THROWHELPERS, THROWNULLREFEXCEPTION, ThrowNullReferenceException, SM_RetVoid)
DEFINE_METHOD(THROWHELPERS, THROWVERIFICATIONEXCEPTION, ThrowVerificationException, SM_Int_RetVoid)
DEFINE_METHOD(THROWHELPERS, THROWAMBIGUOUSRESOLUTIONEXCEPTION, ThrowAmbiguousResolutionException, SM_PtrVoid_PtrVoid_PtrVoid_RetVoid)
Expand Down
50 changes: 0 additions & 50 deletions src/coreclr/vm/fcall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,6 @@
#include "ecall.h"
#include "eeconfig.h"

NOINLINE LPVOID __FCThrow(LPVOID __me, RuntimeExceptionKind reKind, UINT resID, LPCWSTR arg1, LPCWSTR arg2, LPCWSTR arg3)
{
STATIC_CONTRACT_THROWS;
// This isn't strictly true... But the guarantee that we make here is
// that we won't trigger without having setup a frame.
// STATIC_CONTRACT_TRIGGER
STATIC_CONTRACT_GC_NOTRIGGER;

// side effect the compiler can't remove
if (FC_NO_TAILCALL != 1)
return (LPVOID)(SIZE_T)(FC_NO_TAILCALL + 1);

FC_CAN_TRIGGER_GC();
INCONTRACT(FCallCheck __fCallCheck(__FILE__, __LINE__));
FC_GC_POLL_NOT_NEEDED();

HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_NOPOLL(Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
// Now, we can construct & throw.

// In V1, throwing an ExecutionEngineException actually never really threw anything... its was the same as a
// fatal error in the runtime, and we will most probably would have ripped the process down. Starting in
// Whidbey, this behavior has changed a lot. Its not really legal to try to throw an
// ExecutionEngineException with this function.
_ASSERTE((reKind != kExecutionEngineException) ||
!"Don't throw kExecutionEngineException from here. Go to EEPolicy directly, or throw something better.");

#ifdef FEATURE_EH_FUNCLETS
if (g_isNewExceptionHandlingEnabled)
{
DispatchManagedException(reKind);
}
#endif // FEATURE_EH_FUNCLETS

if (resID == 0)
{
// If we have an string to add use NonLocalized otherwise just throw the exception.
if (arg1)
COMPlusThrowNonLocalized(reKind, arg1); //COMPlusThrow(reKind,arg1);
else
COMPlusThrow(reKind);
}
else
COMPlusThrow(reKind, resID, arg1, arg2, arg3);

HELPER_METHOD_FRAME_END();
FC_CAN_TRIGGER_GC_END();
_ASSERTE(!"Throw returned");
return NULL;
}

/**************************************************************************************/
/* erect a frame in the FCALL and then poll the GC, objToProtect will be protected
during the poll and the updated object returned. */
Expand Down
78 changes: 7 additions & 71 deletions src/coreclr/vm/fcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,6 @@
// COMPlusThrow(execpt);
// HELPER_METHOD_FRAME_END()

// It is more efficient (in space) to use convenience macro FCTHROW that does
// this for you (sets up a frame, and does the throw).

// FCTHROW(except)

// Since FCALLS have to conform to the EE calling conventions and not to C
// calling conventions, FCALLS, need to be declared using special macros (FCIMPL*)
// that implement the correct calling conventions. There are variants of these
Expand Down Expand Up @@ -127,16 +122,6 @@
// Consider either using ObjectToOBJECTREF or calling VALIDATEOBJECTREF
// to make sure your Object* is valid.
//
// - FCThrow() must be called directly from your FCall impl function: it
// cannot be called from a subfunction. Calling from a subfunction breaks
// the VC code parsing workaround that lets us recover the callee saved registers.
// Fortunately, you'll get a compile error complaining about an
// unknown variable "__me".
//
// - If your FCall returns VOID, you must use FCThrowVoid() rather than
// FCThrow(). This is because FCThrow() has to generate an unexecuted
// "return" statement for the code parser.
//
// - On x86, if first and/or second argument of your FCall cannot be passed
// in either of the __fastcall registers (ECX/EDX), you must use "V" versions
// of FCDECL and FCIMPL macros to enregister arguments correctly. Some of the
Expand Down Expand Up @@ -175,43 +160,6 @@
// An FCall target uses __fastcall or some other calling convention to
// match the IL calling convention exactly. Thus, a call to FCall is a direct
// call to the target w/ no intervening stub or frame.
//
// The tricky part is when FCThrow is called. FCThrow must generate
// a proper method frame before allocating and throwing the exception.
// To do this, it must recover several things:
//
// - The location of the FCIMPL's return address (since that's
// where the frame will be based.)
//
// - The on-entry values of the callee-saved regs; which must
// be recorded in the frame so that GC can update them.
// Depending on how VC compiles your FCIMPL, those values are still
// in the original registers or saved on the stack.
//
// To figure out which, FCThrow() generates the code:
//
// while (NULL == __FCThrow(__me, ...)) {};
// return 0;
//
// The "return" statement will never execute; but its presence guarantees
// that VC will follow the __FCThrow() call with a VC epilog
// that restores the callee-saved registers using a pretty small
// and predictable set of Intel opcodes. __FCThrow() parses this
// epilog and simulates its execution to recover the callee saved
// registers.
//
// The while loop is to prevent the compiler from doing tail call optimizations.
// The helper frame interpreter needs the frame to be present.
//
// - The MethodDesc* that this FCall implements. This MethodDesc*
// is part of the frame and ensures that the FCall will appear
// in the exception's stack trace. To get this, FCDECL declares
// a static local __me, initialized to point to the FC target itself.
// This address is exactly what's stored in the ECall lookup tables;
// so __FCThrow() simply does a reverse lookup on that table to recover
// the MethodDesc*.
//


#ifndef __FCall_h__
#define __FCall_h__
Expand Down Expand Up @@ -360,12 +308,6 @@ class CompletedFCallTransitionState

#endif // unsupported processor

//==============================================================================================
// This is where FCThrow ultimately ends up. Never call this directly.
// Use the FCThrow() macro.
//==============================================================================================
LPVOID __FCThrow(LPVOID me, enum RuntimeExceptionKind reKind, UINT resID, LPCWSTR arg1, LPCWSTR arg2, LPCWSTR arg3);

//==============================================================================================
// FDECLn: A set of macros for generating header declarations for FC targets.
// Use FIMPLn for the actual body.
Expand Down Expand Up @@ -952,8 +894,7 @@ extern RAW_KEYWORD(volatile) int FC_NO_TAILCALL;
// implementation (use FDECLN for header protos.)
//
// The hidden "__me" variable lets us recover the original MethodDesc*
// so any thrown exceptions will have the correct stack trace. FCThrow()
// passes this along to __FCThrowInternal().
// so any thrown exceptions will have the correct stack trace.
//==============================================================================================

#define GetEEFuncEntryPointMacro(func) ((LPVOID)(func))
Expand Down Expand Up @@ -1162,11 +1103,13 @@ struct FCSigCheck {
#define HCCALL1(funcname, a1) funcname(0, 0, a1)
#define HCCALL1_V(funcname, a1) funcname(0, 0, 0, a1)
#define HCCALL2(funcname, a1, a2) funcname(0, a2, a1)
#define HCCALL2_VV(funcname, a1, a2) funcname(0, 0, 0, a2, a1)
#define HCCALL3(funcname, a1, a2, a3) funcname(0, a2, a1, a3)
#define HCCALL4(funcname, a1, a2, a3, a4) funcname(0, a2, a1, a4, a3)
#define HCCALL5(funcname, a1, a2, a3, a4, a5) funcname(0, a2, a1, a5, a4, a3)
#define HCCALL1_PTR(rettype, funcptr, a1) rettype (F_CALL_CONV * funcptr)(int /* EAX */, int /* EDX */, a1)
#define HCCALL2_PTR(rettype, funcptr, a1, a2) rettype (F_CALL_CONV * funcptr)(int /* EAX */, a2, a1)
#define HCCALL2_VV_PTR(rettype, funcptr, a1, a2) rettype (F_CALL_CONV * funcptr)(int /* EAX */, int /* EDX */, int /* ECX */, a2, a1)
#else // SWIZZLE_REGARG_ORDER

#define HCIMPL0(rettype, funcname) rettype F_CALL_CONV funcname() { HCIMPL_PROLOG(funcname)
Expand All @@ -1186,11 +1129,13 @@ struct FCSigCheck {
#define HCCALL1(funcname, a1) funcname(a1)
#define HCCALL1_V(funcname, a1) funcname(a1)
#define HCCALL2(funcname, a1, a2) funcname(a1, a2)
#define HCCALL2_VV(funcname, a1, a2) funcname(a1, a2)
#define HCCALL3(funcname, a1, a2, a3) funcname(a1, a2, a3)
#define HCCALL4(funcname, a1, a2, a3, a4) funcname(a1, a2, a4, a3)
#define HCCALL5(funcname, a1, a2, a3, a4, a5) funcname(a1, a2, a5, a4, a3)
#define HCCALL1_PTR(rettype, funcptr, a1) rettype (F_CALL_CONV * (funcptr))(a1)
#define HCCALL2_PTR(rettype, funcptr, a1, a2) rettype (F_CALL_CONV * (funcptr))(a1, a2)
#define HCCALL2_VV_PTR(rettype, funcptr, a1, a2) rettype (F_CALL_CONV * (funcptr))(a1, a2)
#endif // !SWIZZLE_REGARG_ORDER
#else // SWIZZLE_STKARG_ORDER

Expand All @@ -1211,29 +1156,20 @@ struct FCSigCheck {
#define HCCALL1(funcname, a1) funcname(a1)
#define HCCALL1_V(funcname, a1) funcname(a1)
#define HCCALL2(funcname, a1, a2) funcname(a1, a2)
#define HCCALL2_VV(funcname, a1, a2) funcname(a1, a2)
#define HCCALL3(funcname, a1, a2, a3) funcname(a1, a2, a3)
#define HCCALL4(funcname, a1, a2, a3, a4) funcname(a1, a2, a3, a4)
#define HCCALL5(funcname, a1, a2, a3, a4, a5) funcname(a1, a2, a3, a4, a5)
#define HCCALL1_PTR(rettype, funcptr, a1) rettype (F_CALL_CONV * (funcptr))(a1)
#define HCCALL2_PTR(rettype, funcptr, a1, a2) rettype (F_CALL_CONV * (funcptr))(a1, a2)
#define HCCALL2_VV_PTR(rettype, funcptr, a1, a2) rettype (F_CALL_CONV * (funcptr))(a1, a2)

#endif // !SWIZZLE_STKARG_ORDER

#define HCIMPLEND_RAW }
#define HCIMPLEND FCALL_TRANSITION_END(); }


//==============================================================================================
// Throws an exception from an FCall. See rexcep.h for a list of valid
// exception codes.
//==============================================================================================
#define FCThrow(reKind) \
{ \
while (NULL == \
__FCThrow(__me, reKind, 0, 0, 0, 0)) {}; \
return 0; \
}

// The managed calling convention expects returned small types (e.g. bool) to be
// widened to 32-bit on return. The C/C++ calling convention does not guarantee returned
// small types to be widened on most platforms. The small types have to be artificially
Expand Down
Loading
Loading