diff --git a/build.sh b/build.sh index 8f6957e..69ac80a 100755 --- a/build.sh +++ b/build.sh @@ -87,7 +87,34 @@ function build_modules() if [ -d "$mod" ] ; then echo Building module $mod pushd $mod - if [ -f module.c ] ; then + mod_sources="" + if [ -f module_params.yml ] ; then + mod_sources="$(yq -r '(.options.sources // [])[] | .value' module_params.yml 2>/dev/null | xargs)" + fi + if [ -n "${mod_sources}" ] ; then + # Multi-file module (options.sources in + # module_params.yml): compile each source as a plain + # ELF object (no LTO - the sources carry their own + # flags from options.cc, e.g. rv64im for the + # soft-float builtins, and must not be re-codegenned + # at LTO link time with the common flags) and merge + # with ld -r. + mod_cflags="${cflags_common}" + yml_cflags="$(yq -r '(.options.cc // [])[] | .value' module_params.yml 2>/dev/null | xargs)" + if [ -n "${yml_cflags}" ] ; then + mod_cflags="--target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu ${yml_cflags}" + fi + rm -rf obj && mkdir -p obj + objs="" + for srcf in ${mod_sources} ; do + of="obj/$(echo "$srcf" | tr / _).o" + clang ${mod_cflags} -DBFLAT_DOTNET=${dotnet_version} -c "$srcf" -o "$of" + on_fail $? "Failed to compile module $mod source $srcf" + objs="$objs $of" + done + riscv64-linux-gnu-ld -r $objs -o module.o + on_fail $? "Failed to merge module $mod objects" + elif [ -f module.c ] ; then # Compile module as C (clang + LTO so lld can do cross-module opt) # -DBFLAT_DOTNET: some module bodies are contract-versioned # against the target runtime (the C/C++ analog of the .S diff --git a/src/bflat/BuildCommand.cs b/src/bflat/BuildCommand.cs index 212bc61..750fd0a 100644 --- a/src/bflat/BuildCommand.cs +++ b/src/bflat/BuildCommand.cs @@ -74,6 +74,7 @@ private BuildCommand() { } private static Option ErrorOnFloatBinaryOption = new Option("--error-on-float-binary", "Scan the LINKED binary's code and fail the build if any RISC-V floating-point (F/D) instruction is present. Exact whole-image check, complements the IL-level --error-on-float."); private static Option ErrorOnCompressedOption = new Option("--error-on-compressed", "Scan the linked binary's code and fail the build if any RISC-V compressed (C-extension, 16-bit) instruction is present. Intended for targets that only decode the base 32-bit encoding, such as zisk."); private static Option ErrorOnAtomicOption = new Option("--error-on-atomic", "Scan the linked binary's code and fail the build if any RISC-V atomic (A-extension: lr/sc/amo*) instruction is present."); + private static Option ZkGcOption = new Option("--zk-gc", "GC of the zkVM guest: 'ugc' (default: NethermindEth/uGC, a bump allocator that never collects) or 'clr' (the upstream workstation GC in the single-threaded runtime flavor, libRuntime.SingleThreaded: no background GC, no finalizer thread; finalizers run from GC.WaitForPendingFinalizers). Requires a runtime pack with dotnet-riscv fixup 30."); private static Option RemoveEhOption = new Option("--remove-eh", "Strip the DWARF unwind tables (.eh_frame, .eh_frame_hdr, .dotnet_eh_table) from the linked zisk image and fail fast on throw instead of dispatching the exception: a throw exits the guest, no catch or finally runs. Saves image size. By default the tables are kept and managed exception handling works."); private static Option NoUnalignedAccessOption = new Option("--no-unaligned-access", "Expand every memory access flagged unaligned into a naturally-aligned byte-wise sequence (RISC-V 64 only). For executors that assert addr % width == 0; costs code size and speed. By default wide unaligned loads/stores are emitted."); private static Option LdFlagsOption = new Option(new string[] { "--ldflags" }, "Arguments to pass to the linker"); @@ -177,6 +178,7 @@ public static Command Create() ErrorOnFloatBinaryOption, ErrorOnCompressedOption, ErrorOnAtomicOption, + ZkGcOption, NoUnalignedAccessOption, RemoveEhOption, }; @@ -207,6 +209,10 @@ static IEnumerable EnumerateExpandedDirectories(string paths, string pat /// arch: riscv64 /// os: linux /// eh: unwind (or failfast; see --remove-eh) + /// fpu: none (or soft / hard; the target's floating-point + /// model: none = rv64ima with the nofp traps, + /// soft = riscv64-lp64 soft-float link, hard = FPU) + /// gc: ugc (or clr; the GC linked into the guest, --zk-gc) /// Conditions are AND-ed; a missing condition does not constrain. The /// parser is deliberately minimal (no YAML dependency) and fails loudly /// on anything it does not understand. @@ -245,6 +251,8 @@ bool ConditionHolds(string key, string val) "arch" => archName, "os" => osName, "eh" => ehName, + "fpu" => _fpuName, + "gc" => _gcName, _ => throw new Exception($"{paramsPath}: unknown condition '{key}'"), }; foreach (string candidate in val.Trim().TrimStart('[').TrimEnd(']').Split(',')) @@ -281,7 +289,7 @@ bool ConditionHolds(string key, string val) string rest = line.Substring(colon + 1).Trim(); // Condition attached to the current list entry. - if (pendingKind != null && (k == "libc" || k == "arch" || k == "os" || k == "eh")) + if (pendingKind != null && (k == "libc" || k == "arch" || k == "os" || k == "eh" || k == "fpu" || k == "gc")) { pendingApplies &= ConditionHolds(k, rest); continue; @@ -458,7 +466,7 @@ void PatchRiscvAbiRuntimeBlobs(string libDir, bool verbose) { "libSystem.Native.a", "libatomic.a", "libeventpipe-disabled.a", "libaotminipal.a", "libstandalonegc-disabled.a", "libstdc++compat.a", - "libRuntime.WorkstationGC.a", "libSystem.IO.Compression.Native.a", + "libRuntime.WorkstationGC.a", "libRuntime.SingleThreaded.a", "libSystem.IO.Compression.Native.a", "libSystem.Security.Cryptography.Native.OpenSsl.a", "libSystem.Globalization.Native.a", }; @@ -480,6 +488,21 @@ void PatchRiscvAbiRuntimeBlobs(string libDir, bool verbose) /// private bool _removeEh; + /// + /// The target's floating-point model, read by the params.yml "fpu:" + /// condition: "none" (zisk/zisk_sim rv64ima, FP trapped by the nofp module), + /// "soft" (zisk/zisk_sim soft-float link, see zkSoftFloat) or "hard" (an + /// FPU target). Latched once the instruction-set support is resolved. + /// + private string _fpuName = "hard"; + + /// + /// The GC linked into the guest, read by the params.yml "gc:" condition: + /// "ugc" (zisk/zisk_sim default) or "clr" (the upstream GC; also every + /// non-zkVM target). Latched next to _fpuName. + /// + private string _gcName = "clr"; + public override int Handle(ParseResult result) { _removeEh = result.GetValueForOption(RemoveEhOption); @@ -656,12 +679,83 @@ public override int Handle(ParseResult result) bool supportsReflection = !disableReflection && systemModuleName == DefaultSystemModule; string isaArg = result.GetValueForOption(TargetIsaOption); + + // Whether the compiler package models the RISC-V extensions as + // instruction sets (the .NET 11 soft-float line does; .NET 10 + // packages know only base/zba/zbb). Probed on a throwaway builder so + // the same bflat sources serve both runtime generations. + bool riscvIsaAware = targetArchitecture == TargetArchitecture.RiscV64 + && new InstructionSetSupportBuilder(targetArchitecture).AddSupportedInstructionSet("f"); + + if (riscvIsaAware && (libc == "zisk" || libc == "zisk_sim")) + { + // zkVM guests target rv64im: drop the C, A, F and D extensions at + // the instruction-set level. Losing F flips ilc and the JIT into + // the lp64 soft-float ABI + soft-float lowering (runtime patches + // 26-28); C and A stop compressed/atomic emission through the + // same instruction-set mechanism as the EnableRiscV64* knobs + // passed below. + if (Environment.GetEnvironmentVariable("BFLAT_NO_ZK_ISA_REDUCTION") == "1") + goto skipReduction; + const string reducedIsa = "-c,-a,-f,-d,-zba,-zbb,-zbs,-zicond"; + isaArg = string.IsNullOrEmpty(isaArg) ? reducedIsa : isaArg + "," + reducedIsa; + skipReduction: ; + } + InstructionSetSupport instructionSetSupport = Helpers.ConfigureInstructionSetSupport(isaArg, maxVectorTBitWidth: 0, isVectorTOptimistic: false, targetArchitecture, tsTargetOs, "Unrecognized instruction set {0}", "Unsupported combination of instruction sets: {0}/{1}", logger, optimizingForSize: optimizationMode == OptimizationMode.PreferSize); + // Soft-float mode: a riscv64 target without the F extension. The JIT + // lowers FP to soft-float calls (dotnet-riscv fixups 26-28); the link + // switches from the nofp trap module to the softfloat builtins module. + // Resolved reflectively: the enum member only exists in compiler + // packages that model F/D/C/A (see riscvIsaAware); on older packages + // this stays false and the classic nofp path is used. + bool zkSoftFloat = false; + if (riscvIsaAware) + { + foreach (var extName in new[] { "RiscV64_F", "RiscV64_D" }) + { + var extField = typeof(Internal.JitInterface.InstructionSet).GetField(extName); + if (extField != null) + { + var extSet = (Internal.JitInterface.InstructionSet)extField.GetValue(null); + zkSoftFloat |= !instructionSetSupport.IsInstructionSetSupported(extSet); + } + } + } + + // The FP-elimination surface (nofp substitutions, C# snippets, the + // CustomILProvider rewrites) belongs to the FPU-less link only. The + // --verify-substitutions probe checks that surface against the CoreLib + // in front of us regardless of the link's FP model, so it always sees it. + bool zkvmTarget = libc == "zisk" || libc == "zisk_sim"; + bool zkNofpSurface = zkvmTarget && + (!zkSoftFloat || result.GetValueForOption(VerifySubstitutionsOption)); + _fpuName = !zkvmTarget ? "hard" : zkNofpSurface ? "none" : "soft"; + string zkGc = (result.GetValueForOption(ZkGcOption) ?? "ugc").ToLowerInvariant(); + if (zkGc != "ugc" && zkGc != "clr") + { + Console.Error.WriteLine($"Error: --zk-gc must be 'ugc' or 'clr' (got '{zkGc}')"); + return 1; + } + _gcName = zkvmTarget ? zkGc : "clr"; + bool zkClrGc = zkvmTarget && _gcName == "clr"; + var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); var targetAbi = TargetAbi.NativeAot; + if (zkSoftFloat) + { + // The ABI is a property of the target, not of the instruction set: the + // compiler selects the lp64 (soft-float) calling convention, the JIT flag + // and the ELF float-ABI marker from TargetAbi, which the runtime, libm and + // compiler-rt of the zisk toolchain (all built with -mabi=lp64) match. + // Resolved reflectively like the instruction sets above; on packages + // without the member the ISA-derived flag of that package applies. + if (Enum.TryParse(typeof(TargetAbi), "NativeAotRiscV64SoftFloat", out object softAbi)) + targetAbi = (TargetAbi)softAbi; + } var targetDetails = new TargetDetails(targetArchitecture, tsTargetOs, targetAbi, simdVectorLength); var ms = new MemoryStream(); @@ -768,8 +862,10 @@ public override int Handle(ParseResult result) CompilerTypeSystemContext typeSystemContext = new BflatTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0); + // The zkVM IL rewrites strip floating point from CoreLib bodies for the + // FPU-less (rv64ima) link; a soft-float link keeps the original bodies. CustomILProvider customIlProvider = new CustomILProvider(ilProviderOld, typeSystemContext, - isZkvmTarget: libc == "zisk" || libc == "zisk_sim"); + isZkvmTarget: zkNofpSurface); ILProvider ilProvider = customIlProvider; var referenceFilePaths = new Dictionary(); @@ -972,7 +1068,20 @@ public override int Handle(ParseResult result) if (stdlib == StandardLibType.DotNet) { compilationRoots.Add(new RuntimeConfigurationRootProvider("g_compilerEmbeddedSettingsBlob", Array.Empty())); - compilationRoots.Add(new RuntimeConfigurationRootProvider("g_compilerEmbeddedKnobsBlob", Array.Empty())); + // Runtime knobs (runtimeconfig.json equivalents, read by RhConfig). + // The upstream GC on the zkVM guest: the guest has no virtual memory + // (pal's mmap is a bump allocation), so the region reservation and + // the heap hard limit must fit the RAM window of script.ld instead + // of the 256 GB / 2x-physical defaults. + string[] runtimeKnobs = zkClrGc + ? new[] + { + "System.GC.Concurrent=false", + "System.GC.HeapHardLimit=100663296", // 96 MB + "System.GC.RegionRange=134217728", // 128 MB, reserved eagerly from the bump heap + } + : Array.Empty(); + compilationRoots.Add(new RuntimeConfigurationRootProvider("g_compilerEmbeddedKnobsBlob", runtimeKnobs)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } else @@ -1332,6 +1441,17 @@ public override int Handle(ParseResult result) { backendOptions.Add("EnableRiscV64Compressed=0"); backendOptions.Add("EnableRiscV64Atomic=0"); + // The cross-JIT resolves ISA availability through the altjit path + // (unmatched VM), where every EnableRiscV64* knob defaults to on - + // independent of the ilc instruction-set support. Turn off + // everything the zkVM cannot decode; F/D for hygiene (the + // soft-float lowering is driven by SOFTFP_ABI, not these). + backendOptions.Add("EnableRiscV64Zicond=0"); + backendOptions.Add("EnableRiscV64Zba=0"); + backendOptions.Add("EnableRiscV64Zbb=0"); + backendOptions.Add("EnableRiscV64Zbs=0"); + backendOptions.Add("EnableRiscV64F=0"); + backendOptions.Add("EnableRiscV64D=0"); } // Unaligned access. dotnet-riscv fixup 35 (JitNoUnalignedAccess) defaults the @@ -1776,7 +1896,10 @@ or TargetArchitecture.RiscV64 PatchRiscvAbiRuntimeBlobs(firstLib, verbose); ldArgs.Append("-leventpipe-disabled "); ldArgs.Append("-laotminipal -lstandalonegc-disabled "); - ldArgs.Append("-lstdc++compat -lRuntime.WorkstationGC -lSystem.IO.Compression.Native -lSystem.Security.Cryptography.Native.OpenSsl "); + // zkVM + --zk-gc clr: the single-threaded flavor of the + // runtime (dotnet-riscv fixup 30) carries the upstream GC. + ldArgs.Append(zkClrGc ? "-lRuntime.SingleThreaded " : "-lRuntime.WorkstationGC "); + ldArgs.Append("-lstdc++compat -lSystem.IO.Compression.Native -lSystem.Security.Cryptography.Native.OpenSsl "); if (libc != "bionic") ldArgs.Append("-lSystem.Globalization.Native "); } @@ -1872,11 +1995,28 @@ or TargetArchitecture.RiscV64 ldArgs.Append($"-T\"{Path.Combine(ziskSimLibPath, "script.ld")}\" "); } ldArgs.Append($"\"{Path.Combine(ziskLibPath, "entrypoint.o")}\" "); - /* nofp: FP trap stubs; the math-symbol wrap surface is - * declared in nofp.params.yml. (The musl target links the - * object without these wraps.) */ - ldArgs.Append($"\"{Path.Combine(ziskLibPath, "nofp.o")}\" "); - AppendModuleParams(ldArgs, ziskLibPath, "nofp", libc, targetArchitecture, targetOS); + if (zkSoftFloat) + { + /* softfloat: real soft-float builtins (compiler-rt, + * rv64im) plus soft replacements for the hard-float + * runtime conversion helpers and fmod; the wrap surface + * is declared in softfloat.params.yml. The nofp trap + * module and its libm wraps are NOT linked: FP works. + * Note: the libm surface itself (sin, sqrt, formatting + * via fmt_fp) still resolves to hard-float musl members + * and is caught by --error-on-float-binary until a + * soft-float libm ships. */ + ldArgs.Append($"\"{Path.Combine(ziskLibPath, "softfloat.o")}\" "); + AppendModuleParams(ldArgs, ziskLibPath, "softfloat", libc, targetArchitecture, targetOS); + } + else + { + /* nofp: FP trap stubs; the math-symbol wrap surface is + * declared in nofp.params.yml. (The musl target links the + * object without these wraps.) */ + ldArgs.Append($"\"{Path.Combine(ziskLibPath, "nofp.o")}\" "); + AppendModuleParams(ldArgs, ziskLibPath, "nofp", libc, targetArchitecture, targetOS); + } ldArgs.Append($"--whole-archive "); ldArgs.Append($"\"{Path.Combine(ziskLibPath, "ubootstrap.o")}\" "); ldArgs.Append($"\"{Path.Combine(ziskLibPath, "stdcppshim.o")}\" "); @@ -1927,7 +2067,9 @@ or TargetArchitecture.RiscV64 ldArgs.Append($"\"{Path.Combine(ziskLibPath, "rust_sys.o")}\" "); AppendModuleParams(ldArgs, ziskLibPath, "rust_sys", libc, targetArchitecture, targetOS); - /* ugc */ + /* ugc (unless --zk-gc clr links the upstream GC) */ + if (_gcName == "ugc") + { AppendModuleParams(ldArgs, ziskLibPath, "ugc-zero", libc, targetArchitecture, targetOS); ldArgs.Append($"\"{Path.Combine(ziskLibPath, "uGC.cpp.obj")}\" "); ldArgs.Append($"\"{Path.Combine(ziskLibPath, "uGCHandleManager.cpp.obj")}\" "); @@ -1937,6 +2079,7 @@ or TargetArchitecture.RiscV64 * verified C core shipped as separate objects. */ ldArgs.Append($"\"{Path.Combine(ziskLibPath, "ugc_core.c.obj")}\" "); ldArgs.Append($"\"{Path.Combine(ziskLibPath, "ugc_zalloc.c.obj")}\" "); + } } } @@ -1983,6 +2126,18 @@ static int RunCommand(string command, string args, bool print) outputFilePath + " " + patchedFilePath + patchElfArgs, printCommands); + if (patchExitCode != 0) + { + Console.Error.WriteLine("ELF postprocessing failed"); + return patchExitCode; + } + // patch_elf writes its result to patchedFilePath; put it where the + // caller asked. Historically the guest recipes passed + // -o .patched, which made ChangeExtension a no-op and the + // script overwrite its input in place - any other output name left + // the final file UNPROCESSED (and unbootable on the zkVM). + if (!string.Equals(patchedFilePath, outputFilePath, StringComparison.Ordinal)) + File.Move(patchedFilePath, outputFilePath, overwrite: true); } // Exact whole-image ISA verification: decode the linked binary and fail diff --git a/src/bflat/ExtLibResolver.cs b/src/bflat/ExtLibResolver.cs index a391def..2efef47 100644 --- a/src/bflat/ExtLibResolver.cs +++ b/src/bflat/ExtLibResolver.cs @@ -368,6 +368,22 @@ private static Result ParseManifestAndResolveFiles( if (staticLibRel != null) { string absPath = Path.Combine(manifestDir, staticLibRel.Replace('/', Path.DirectorySeparatorChar)); + if (!File.Exists(absPath)) + { + // NuGet layout: the manifest lives under contentFiles/any/any/ + // while runtimes//native/ sits at the package root, so a + // manifest-relative path misses it. Walk up towards the + // extraction root and take the first match. + for (string dir = manifestDir; dir != null; dir = Path.GetDirectoryName(dir)) + { + string candidate = Path.Combine(dir, staticLibRel.Replace('/', Path.DirectorySeparatorChar)); + if (File.Exists(candidate)) + { + absPath = candidate; + break; + } + } + } if (!File.Exists(absPath)) throw new Exception( $"Static library not found: {absPath} (referenced from manifest '{manifestPath}')"); diff --git a/src/bflat/InstructionSetHelpers.cs b/src/bflat/InstructionSetHelpers.cs index 250cd98..c7016a9 100644 --- a/src/bflat/InstructionSetHelpers.cs +++ b/src/bflat/InstructionSetHelpers.cs @@ -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; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; @@ -69,6 +70,19 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication } } + else if (targetArchitecture == TargetArchitecture.RiscV64 + && Environment.GetEnvironmentVariable("BFLAT_NO_RISCV_BASELINE") != "1") + { + // The rv64gc baseline: "d" implies "f" and "base"; "c" and "a" + // complete the G+C set. Reduced-ISA targets opt out with + // --targetisa negation (e.g. -f,-d) - an unsupported F extension + // switches ilc and the JIT to the lp64 soft-float ABI and + // soft-float lowering. + instructionSetSupportBuilder.AddSupportedInstructionSet("base"); + instructionSetSupportBuilder.AddSupportedInstructionSet("d"); + instructionSetSupportBuilder.AddSupportedInstructionSet("c"); + instructionSetSupportBuilder.AddSupportedInstructionSet("a"); + } // Whether to allow optimistically expanding the instruction sets beyond what was specified. // We seed this from optimizingForSize - if we're size-optimizing, we don't want to unnecessarily diff --git a/src/bflat/ZkvmIL.cs b/src/bflat/ZkvmIL.cs index ee3ea82..5d066bb 100644 --- a/src/bflat/ZkvmIL.cs +++ b/src/bflat/ZkvmIL.cs @@ -448,7 +448,7 @@ public override MethodIL GetMethodIL(MethodDesc method) // polynomial used only by float FORMATTING). RyuJIT materializes each float // constant with flw/fld + fsw/fsd - this is the dominant FP in a .NET 11 image // (~1536 instructions). The table's sole reader is Number.DiyFp128Sqrt, reachable - // only through Number.FormatFloat, which zisk.substitutions.xml removes - so the + // only through Number.FormatFloat, which zisk.nofp.substitutions.xml removes - so the // table is dead. Keep the allocation (a zeroed SqrtCoefficients[256] - harmless // even if ever indexed) but nop out the constant-filling body between the newarr // and the stsfld: the array stays on the stack across the nops, so the store is diff --git a/src/bflat/ZkvmSubstitutions.cs b/src/bflat/ZkvmSubstitutions.cs index c2ce787..ad822f7 100644 --- a/src/bflat/ZkvmSubstitutions.cs +++ b/src/bflat/ZkvmSubstitutions.cs @@ -175,7 +175,7 @@ MethodDesc Snippet(string name) // later (--error-on-float, or the emulator rejecting an F/D opcode). // That already happened once - Number.FormatFloat's signature changed // in .NET 11, the entry stopped matching, and floating point came back - // (see the note in zisk.substitutions.xml). So a missing target is + // (see the note in zisk.nofp.substitutions.xml). So a missing target is // fatal, and the few genuinely conditional ones must say so. void Add(MethodDesc target, MethodDesc snippet, string what) { @@ -268,7 +268,7 @@ ModuleDesc Module(string simpleName) // System.Double / System.Single: the object overloads of Equals and // CompareTo. They cannot be body="remove"d (see the note in - // zisk.substitutions.xml - they are what ValueType.Equals and the + // zisk.nofp.substitutions.xml - they are what ValueType.Equals and the // generic comparers reach for), and the originals compare in FP // registers, so they are the one FP source that survives into an // ordinary guest. The snippets redo the same IEEE comparison on the raw diff --git a/src/bflat/bflat.csproj b/src/bflat/bflat.csproj index a8e58d3..0b42925 100644 --- a/src/bflat/bflat.csproj +++ b/src/bflat/bflat.csproj @@ -128,6 +128,12 @@ + + + + @@ -209,6 +215,8 @@ DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\zisk_subst.params.yml" /> + diff --git a/src/bflat/bflat.variant.props b/src/bflat/bflat.variant.props index 6b75640..b4341af 100644 --- a/src/bflat/bflat.variant.props +++ b/src/bflat/bflat.variant.props @@ -43,7 +43,7 @@ .p3 .x10 - .x9 + .x13-sf <--- lower half ---> + // [x_UQ0_hw * b_UQ1_hw] + // + [ x_UQ0_hw * blo ] + // - [ b_UQ1 ] + // = [ result ][.... discarded ...] + rep_t corr_UQ1 = 0U - ( (rep_t)x_UQ0_hw * b_UQ1_hw + + ((rep_t)x_UQ0_hw * blo >> HW) + - REP_C(1)); // account for *possible* carry + rep_t lo_corr = corr_UQ1 & loMask; + rep_t hi_corr = corr_UQ1 >> HW; + // x_UQ0 * corr_UQ1 = (x_UQ0_hw * 2^HW) * (hi_corr * 2^HW + lo_corr) - corr_UQ1 + x_UQ0 = ((rep_t)x_UQ0_hw * hi_corr << 1) + + ((rep_t)x_UQ0_hw * lo_corr >> (HW - 1)) + - REP_C(2); // 1 to account for the highest bit of corr_UQ1 can be 1 + // 1 to account for possible carry + // Just like the case of half-width iterations but with possibility + // of overflowing by one extra Ulp of x_UQ0. + x_UQ0 -= 1U; + // ... and then traditional fixup by 2 should work + + // On error estimation: + // abs(E_{N-1}) <= (u_{N-1} + 2 /* due to conversion e_n -> E_n */) * 2^-HW + // + (2^-HW + 2^-W)) + // abs(E_{N-1}) <= (u_{N-1} + 3.01) * 2^-HW + + // Then like for the half-width iterations: + // With 0 <= eps1, eps2 < 2^-W + // E_N = 4 * E_{N-1} * eps1 - (E_{N-1}^2 * b + 4 * eps2) + 4 * eps1 / b + // abs(E_N) <= 2^-W * [ 4 * abs(E_{N-1}) + max(2 * abs(E_{N-1})^2 * 2^W + 4, 8)) ] + // abs(E_N) <= 2^-W * [ 4 * (u_{N-1} + 3.01) * 2^-HW + max(4 + 2 * (u_{N-1} + 3.01)^2, 8) ] +#endif + + // Finally, account for possible overflow, as explained above. + x_UQ0 -= 2U; + + // u_n for different precisions (with N-1 half-width iterations): + // W0 is the precision of C + // u_0 = (3/4 - 1/sqrt(2) + 2^-W0) * 2^HW + + // Estimated with bc: + // define half1(un) { return 2.0 * (un + un^2) / 2.0^hw + 1.0; } + // define half2(un) { return 2.0 * un / 2.0^hw + 2.0; } + // define full1(un) { return 4.0 * (un + 3.01) / 2.0^hw + 2.0 * (un + 3.01)^2 + 4.0; } + // define full2(un) { return 4.0 * (un + 3.01) / 2.0^hw + 8.0; } + + // | f32 (0 + 3) | f32 (2 + 1) | f64 (3 + 1) | f128 (4 + 1) + // u_0 | < 184224974 | < 2812.1 | < 184224974 | < 791240234244348797 + // u_1 | < 15804007 | < 242.7 | < 15804007 | < 67877681371350440 + // u_2 | < 116308 | < 2.81 | < 116308 | < 499533100252317 + // u_3 | < 7.31 | | < 7.31 | < 27054456580 + // u_4 | | | | < 80.4 + // Final (U_N) | same as u_3 | < 72 | < 218 | < 13920 + + // Add 2 to U_N due to final decrement. + +#if defined(SINGLE_PRECISION) && NUMBER_OF_HALF_ITERATIONS == 2 && NUMBER_OF_FULL_ITERATIONS == 1 +#define RECIPROCAL_PRECISION REP_C(74) +#elif defined(SINGLE_PRECISION) && NUMBER_OF_HALF_ITERATIONS == 0 && NUMBER_OF_FULL_ITERATIONS == 3 +#define RECIPROCAL_PRECISION REP_C(10) +#elif defined(DOUBLE_PRECISION) && NUMBER_OF_HALF_ITERATIONS == 3 && NUMBER_OF_FULL_ITERATIONS == 1 +#define RECIPROCAL_PRECISION REP_C(220) +#elif defined(QUAD_PRECISION) && NUMBER_OF_HALF_ITERATIONS == 4 && NUMBER_OF_FULL_ITERATIONS == 1 +#define RECIPROCAL_PRECISION REP_C(13922) +#else +#error Invalid number of iterations +#endif + + // Suppose 1/b - P * 2^-W < x < 1/b + P * 2^-W + x_UQ0 -= RECIPROCAL_PRECISION; + // Now 1/b - (2*P) * 2^-W < x < 1/b + // FIXME Is x_UQ0 still >= 0.5? + + rep_t quotient_UQ1, dummy; + wideMultiply(x_UQ0, aSignificand << 1, "ient_UQ1, &dummy); + // Now, a/b - 4*P * 2^-W < q < a/b for q= in UQ1.(SB+1+W). + + // quotient_UQ1 is in [0.5, 2.0) as UQ1.(SB+1), + // adjust it to be in [1.0, 2.0) as UQ1.SB. + rep_t residualLo; + if (quotient_UQ1 < (implicitBit << 1)) { + // Highest bit is 0, so just reinterpret quotient_UQ1 as UQ1.SB, + // effectively doubling its value as well as its error estimation. + residualLo = (aSignificand << (significandBits + 1)) - quotient_UQ1 * bSignificand; + writtenExponent -= 1; + aSignificand <<= 1; + } else { + // Highest bit is 1 (the UQ1.(SB+1) value is in [1, 2)), convert it + // to UQ1.SB by right shifting by 1. Least significant bit is omitted. + quotient_UQ1 >>= 1; + residualLo = (aSignificand << significandBits) - quotient_UQ1 * bSignificand; + } + // NB: residualLo is calculated above for the normal result case. + // It is re-computed on denormal path that is expected to be not so + // performance-sensitive. + + // Now, q cannot be greater than a/b and can differ by at most 8*P * 2^-W + 2^-SB + // Each NextAfter() increments the floating point value by at least 2^-SB + // (more, if exponent was incremented). + // Different cases (<---> is of 2^-SB length, * = a/b that is shown as a midpoint): + // q + // | | * | | | | | + // <---> 2^t + // | | | | | * | | + // q + // To require at most one NextAfter(), an error should be less than 1.5 * 2^-SB. + // (8*P) * 2^-W + 2^-SB < 1.5 * 2^-SB + // (8*P) * 2^-W < 0.5 * 2^-SB + // P < 2^(W-4-SB) + // Generally, for at most R NextAfter() to be enough, + // P < (2*R - 1) * 2^(W-4-SB) + // For f32 (0+3): 10 < 32 (OK) + // For f32 (2+1): 32 < 74 < 32 * 3, so two NextAfter() are required + // For f64: 220 < 256 (OK) + // For f128: 4096 * 3 < 13922 < 4096 * 5 (three NextAfter() are required) + + // If we have overflowed the exponent, return infinity + if (writtenExponent >= maxExponent) + return fromRep(infRep | quotientSign); + + // Now, quotient_UQ1_SB <= the correctly-rounded result + // and may need taking NextAfter() up to 3 times (see error estimates above) + // r = a - b * q + rep_t absResult; + if (writtenExponent > 0) { + // Clear the implicit bit + absResult = quotient_UQ1 & significandMask; + // Insert the exponent + absResult |= (rep_t)writtenExponent << significandBits; + residualLo <<= 1; + } else { + // Prevent shift amount from being negative + if (significandBits + writtenExponent < 0) + return fromRep(quotientSign); + + absResult = quotient_UQ1 >> (-writtenExponent + 1); + + // multiplied by two to prevent shift amount to be negative + residualLo = (aSignificand << (significandBits + writtenExponent)) - (absResult * bSignificand << 1); + } + + // Round + residualLo += absResult & 1; // tie to even + // The above line conditionally turns the below LT comparison into LTE + absResult += residualLo > bSignificand; +#if defined(QUAD_PRECISION) || (defined(SINGLE_PRECISION) && NUMBER_OF_HALF_ITERATIONS > 0) + // Do not round Infinity to NaN + absResult += absResult < infRep && residualLo > (2 + 1) * bSignificand; +#endif +#if defined(QUAD_PRECISION) + absResult += absResult < infRep && residualLo > (4 + 1) * bSignificand; +#endif + return fromRep(absResult | quotientSign); +} diff --git a/src/bflat/modules/softfloat/builtins/fp_extend.h b/src/bflat/modules/softfloat/builtins/fp_extend.h new file mode 100644 index 0000000..95ea2a7 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_extend.h @@ -0,0 +1,174 @@ +//===-lib/fp_extend.h - low precision -> high precision conversion -*- C +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Set source and destination setting +// +//===----------------------------------------------------------------------===// + +#ifndef FP_EXTEND_HEADER +#define FP_EXTEND_HEADER + +#include "int_lib.h" + +#if defined SRC_SINGLE +typedef float src_t; +typedef uint32_t src_rep_t; +#define SRC_REP_C UINT32_C +static const int srcBits = sizeof(src_t) * CHAR_BIT; +static const int srcSigFracBits = 23; +// -1 accounts for the sign bit. +// srcBits - srcSigFracBits - 1 +static const int srcExpBits = 8; +#define src_rep_t_clz clzsi + +#elif defined SRC_DOUBLE +typedef double src_t; +typedef uint64_t src_rep_t; +#define SRC_REP_C UINT64_C +static const int srcBits = sizeof(src_t) * CHAR_BIT; +static const int srcSigFracBits = 52; +// -1 accounts for the sign bit. +// srcBits - srcSigFracBits - 1 +static const int srcExpBits = 11; + +static inline int src_rep_t_clz_impl(src_rep_t a) { +#if defined __LP64__ + return __builtin_clzl(a); +#else + if (a & REP_C(0xffffffff00000000)) + return clzsi(a >> 32); + else + return 32 + clzsi(a & REP_C(0xffffffff)); +#endif +} +#define src_rep_t_clz src_rep_t_clz_impl + +#elif defined SRC_80 +typedef xf_float src_t; +typedef __uint128_t src_rep_t; +#define SRC_REP_C (__uint128_t) +// sign bit, exponent and significand occupy the lower 80 bits. +static const int srcBits = 80; +static const int srcSigFracBits = 63; +// -1 accounts for the sign bit. +// -1 accounts for the explicitly stored integer bit. +// srcBits - srcSigFracBits - 1 - 1 +static const int srcExpBits = 15; + +#elif defined SRC_HALF +#ifdef COMPILER_RT_HAS_FLOAT16 +typedef _Float16 src_t; +#else +typedef uint16_t src_t; +#endif +typedef uint16_t src_rep_t; +#define SRC_REP_C UINT16_C +static const int srcBits = sizeof(src_t) * CHAR_BIT; +static const int srcSigFracBits = 10; +// -1 accounts for the sign bit. +// srcBits - srcSigFracBits - 1 +static const int srcExpBits = 5; + +static inline int src_rep_t_clz_impl(src_rep_t a) { + return __builtin_clz(a) - 16; +} + +#define src_rep_t_clz src_rep_t_clz_impl + +#else +#error Source should be half, single, or double precision! +#endif // end source precision + +#if defined DST_SINGLE +typedef float dst_t; +typedef uint32_t dst_rep_t; +#define DST_REP_C UINT32_C +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 23; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 8; + +#elif defined DST_DOUBLE +typedef double dst_t; +typedef uint64_t dst_rep_t; +#define DST_REP_C UINT64_C +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 52; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 11; + +#elif defined DST_QUAD +typedef tf_float dst_t; +typedef __uint128_t dst_rep_t; +#define DST_REP_C (__uint128_t) +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 112; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 15; + +#else +#error Destination should be single, double, or quad precision! +#endif // end destination precision + +// End of specialization parameters. + +// TODO: These helper routines should be placed into fp_lib.h +// Currently they depend on macros/constants defined above. + +static inline src_rep_t extract_sign_from_src(src_rep_t x) { + const src_rep_t srcSignMask = SRC_REP_C(1) << (srcBits - 1); + return (x & srcSignMask) >> (srcBits - 1); +} + +static inline src_rep_t extract_exp_from_src(src_rep_t x) { + const int srcSigBits = srcBits - 1 - srcExpBits; + const src_rep_t srcExpMask = ((SRC_REP_C(1) << srcExpBits) - 1) << srcSigBits; + return (x & srcExpMask) >> srcSigBits; +} + +static inline src_rep_t extract_sig_frac_from_src(src_rep_t x) { + const src_rep_t srcSigFracMask = (SRC_REP_C(1) << srcSigFracBits) - 1; + return x & srcSigFracMask; +} + +#ifdef src_rep_t_clz +static inline int clz_in_sig_frac(src_rep_t sigFrac) { + const int skip = 1 + srcExpBits; + return src_rep_t_clz(sigFrac) - skip; +} +#endif + +static inline dst_rep_t construct_dst_rep(dst_rep_t sign, dst_rep_t exp, dst_rep_t sigFrac) { + return (sign << (dstBits - 1)) | (exp << (dstBits - 1 - dstExpBits)) | sigFrac; +} + +// Two helper routines for conversion to and from the representation of +// floating-point data as integer values follow. + +static inline src_rep_t srcToRep(src_t x) { + const union { + src_t f; + src_rep_t i; + } rep = {.f = x}; + return rep.i; +} + +static inline dst_t dstFromRep(dst_rep_t x) { + const union { + dst_t f; + dst_rep_t i; + } rep = {.i = x}; + return rep.f; +} +// End helper routines. Conversion implementation follows. + +#endif // FP_EXTEND_HEADER diff --git a/src/bflat/modules/softfloat/builtins/fp_extend_impl.inc b/src/bflat/modules/softfloat/builtins/fp_extend_impl.inc new file mode 100644 index 0000000..f4f6630 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_extend_impl.inc @@ -0,0 +1,108 @@ +//=-lib/fp_extend_impl.inc - low precision -> high precision conversion -*-- -// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a fairly generic conversion from a narrower to a wider +// IEEE-754 floating-point type. The constants and types defined following the +// includes below parameterize the conversion. +// +// It does not support types that don't use the usual IEEE-754 interchange +// formats; specifically, some work would be needed to adapt it to +// (for example) the Intel 80-bit format or PowerPC double-double format. +// +// Note please, however, that this implementation is only intended to support +// *widening* operations; if you need to convert to a *narrower* floating-point +// type (e.g. double -> float), then this routine will not do what you want it +// to. +// +// It also requires that integer types at least as large as both formats +// are available on the target platform; this may pose a problem when trying +// to add support for quad on some 32-bit systems, for example. You also may +// run into trouble finding an appropriate CLZ function for wide source types; +// you will likely need to roll your own on some platforms. +// +// Finally, the following assumptions are made: +// +// 1. Floating-point types and integer types have the same endianness on the +// target platform. +// +// 2. Quiet NaNs, if supported, are indicated by the leading bit of the +// significand field being set. +// +//===----------------------------------------------------------------------===// + +#include "fp_extend.h" + +// The source type may use a usual IEEE-754 interchange format or Intel 80-bit +// format. In particular, for the source type srcSigFracBits may be not equal to +// srcSigBits. The destination type is assumed to be one of IEEE-754 standard +// types. +static __inline dst_t __extendXfYf2__(src_t a) { + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const int srcInfExp = (1 << srcExpBits) - 1; + const int srcExpBias = srcInfExp >> 1; + + const int dstInfExp = (1 << dstExpBits) - 1; + const int dstExpBias = dstInfExp >> 1; + + // Break a into a sign and representation of the absolute value. + const src_rep_t aRep = srcToRep(a); + const src_rep_t srcSign = extract_sign_from_src(aRep); + const src_rep_t srcExp = extract_exp_from_src(aRep); + const src_rep_t srcSigFrac = extract_sig_frac_from_src(aRep); + + dst_rep_t dstSign = srcSign; + dst_rep_t dstExp; + dst_rep_t dstSigFrac; + + if (srcExp >= 1 && srcExp < (src_rep_t)srcInfExp) { + // a is a normal number. + dstExp = (dst_rep_t)srcExp + (dst_rep_t)(dstExpBias - srcExpBias); + dstSigFrac = (dst_rep_t)srcSigFrac << (dstSigFracBits - srcSigFracBits); + } + + else if (srcExp == srcInfExp) { + // a is NaN or infinity. + dstExp = dstInfExp; + dstSigFrac = (dst_rep_t)srcSigFrac << (dstSigFracBits - srcSigFracBits); + } + + else if (srcSigFrac) { + // a is denormal. + if (srcExpBits == dstExpBits) { + // The exponent fields are identical and this is a denormal number, so all + // the non-significand bits are zero. In particular, this branch is always + // taken when we extend a denormal F80 to F128. + dstExp = 0; + dstSigFrac = ((dst_rep_t)srcSigFrac) << (dstSigFracBits - srcSigFracBits); + } else { +#ifndef src_rep_t_clz + // If src_rep_t_clz is not defined this branch must be unreachable. + __builtin_unreachable(); +#else + // Renormalize the significand and clear the leading bit. + // For F80 -> F128 this codepath is unused. + const int scale = clz_in_sig_frac(srcSigFrac) + 1; + dstExp = dstExpBias - srcExpBias - scale + 1; + dstSigFrac = (dst_rep_t)srcSigFrac + << (dstSigFracBits - srcSigFracBits + scale); + const dst_rep_t dstMinNormal = DST_REP_C(1) << (dstBits - 1 - dstExpBits); + dstSigFrac ^= dstMinNormal; +#endif + } + } + + else { + // a is zero. + dstExp = 0; + dstSigFrac = 0; + } + + const dst_rep_t result = construct_dst_rep(dstSign, dstExp, dstSigFrac); + return dstFromRep(result); +} diff --git a/src/bflat/modules/softfloat/builtins/fp_fixint_impl.inc b/src/bflat/modules/softfloat/builtins/fp_fixint_impl.inc new file mode 100644 index 0000000..3556bad --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_fixint_impl.inc @@ -0,0 +1,40 @@ +//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements float to integer conversion for the +// compiler-rt library. +// +//===----------------------------------------------------------------------===// + +#include "fp_lib.h" + +static __inline fixint_t __fixint(fp_t a) { + const fixint_t fixint_max = (fixint_t)((~(fixuint_t)0) / 2); + const fixint_t fixint_min = -fixint_max - 1; + // Break a into sign, exponent, significand parts. + const rep_t aRep = toRep(a); + const rep_t aAbs = aRep & absMask; + const fixint_t sign = aRep & signBit ? -1 : 1; + const int exponent = (aAbs >> significandBits) - exponentBias; + const rep_t significand = (aAbs & significandMask) | implicitBit; + + // If exponent is negative, the result is zero. + if (exponent < 0) + return 0; + + // If the value is too large for the integer type, saturate. + if ((unsigned)exponent >= sizeof(fixint_t) * CHAR_BIT) + return sign == 1 ? fixint_max : fixint_min; + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if (exponent < significandBits) + return sign * (significand >> (significandBits - exponent)); + else + return sign * ((fixuint_t)significand << (exponent - significandBits)); +} diff --git a/src/bflat/modules/softfloat/builtins/fp_fixuint_impl.inc b/src/bflat/modules/softfloat/builtins/fp_fixuint_impl.inc new file mode 100644 index 0000000..cb2bf54 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_fixuint_impl.inc @@ -0,0 +1,38 @@ +//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements float to unsigned integer conversion for the +// compiler-rt library. +// +//===----------------------------------------------------------------------===// + +#include "fp_lib.h" + +static __inline fixuint_t __fixuint(fp_t a) { + // Break a into sign, exponent, significand parts. + const rep_t aRep = toRep(a); + const rep_t aAbs = aRep & absMask; + const int sign = aRep & signBit ? -1 : 1; + const int exponent = (aAbs >> significandBits) - exponentBias; + const rep_t significand = (aAbs & significandMask) | implicitBit; + + // If either the value or the exponent is negative, the result is zero. + if (sign == -1 || exponent < 0) + return 0; + + // If the value is too large for the integer type, saturate. + if ((unsigned)exponent >= sizeof(fixuint_t) * CHAR_BIT) + return ~(fixuint_t)0; + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if (exponent < significandBits) + return significand >> (significandBits - exponent); + else + return (fixuint_t)significand << (exponent - significandBits); +} diff --git a/src/bflat/modules/softfloat/builtins/fp_lib.h b/src/bflat/modules/softfloat/builtins/fp_lib.h new file mode 100644 index 0000000..c4f0a5b --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_lib.h @@ -0,0 +1,425 @@ +//===-- lib/fp_lib.h - Floating-point utilities -------------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a configuration header for soft-float routines in compiler-rt. +// This file does not provide any part of the compiler-rt interface, but defines +// many useful constants and utility routines that are used in the +// implementation of the soft-float routines in compiler-rt. +// +// Assumes that float, double and long double correspond to the IEEE-754 +// binary32, binary64 and binary 128 types, respectively, and that integer +// endianness matches floating point endianness on the target platform. +// +//===----------------------------------------------------------------------===// + +#ifndef FP_LIB_HEADER +#define FP_LIB_HEADER + +#include "int_lib.h" +#include "int_math.h" +#include "int_types.h" +#include +#include +#include + +#if defined SINGLE_PRECISION + +typedef uint16_t half_rep_t; +typedef uint32_t rep_t; +typedef uint64_t twice_rep_t; +typedef int32_t srep_t; +typedef float fp_t; +#define HALF_REP_C UINT16_C +#define REP_C UINT32_C +#define significandBits 23 + +static __inline int rep_clz(rep_t a) { return clzsi(a); } + +// 32x32 --> 64 bit multiply +static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { + const uint64_t product = (uint64_t)a * b; + *hi = product >> 32; + *lo = product; +} +COMPILER_RT_ABI fp_t __addsf3(fp_t a, fp_t b); + +#elif defined DOUBLE_PRECISION + +typedef uint32_t half_rep_t; +typedef uint64_t rep_t; +typedef int64_t srep_t; +typedef double fp_t; +#define HALF_REP_C UINT32_C +#define REP_C UINT64_C +#define significandBits 52 + +static __inline int rep_clz(rep_t a) { +#if defined __LP64__ + return __builtin_clzl(a); +#else + if (a & REP_C(0xffffffff00000000)) + return clzsi(a >> 32); + else + return 32 + clzsi(a & REP_C(0xffffffff)); +#endif +} + +#define loWord(a) (a & 0xffffffffU) +#define hiWord(a) (a >> 32) + +// 64x64 -> 128 wide multiply for platforms that don't have such an operation; +// many 64-bit platforms have this operation, but they tend to have hardware +// floating-point, so we don't bother with a special case for them here. +static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { + // Each of the component 32x32 -> 64 products + const uint64_t plolo = loWord(a) * loWord(b); + const uint64_t plohi = loWord(a) * hiWord(b); + const uint64_t philo = hiWord(a) * loWord(b); + const uint64_t phihi = hiWord(a) * hiWord(b); + // Sum terms that contribute to lo in a way that allows us to get the carry + const uint64_t r0 = loWord(plolo); + const uint64_t r1 = hiWord(plolo) + loWord(plohi) + loWord(philo); + *lo = r0 + (r1 << 32); + // Sum terms contributing to hi with the carry from lo + *hi = hiWord(plohi) + hiWord(philo) + hiWord(r1) + phihi; +} +#undef loWord +#undef hiWord + +COMPILER_RT_ABI fp_t __adddf3(fp_t a, fp_t b); + +#elif defined QUAD_PRECISION +#if defined(CRT_HAS_F128) && defined(CRT_HAS_128BIT) +typedef uint64_t half_rep_t; +typedef __uint128_t rep_t; +typedef __int128_t srep_t; +typedef tf_float fp_t; +#define HALF_REP_C UINT64_C +#define REP_C (__uint128_t) +#if defined(CRT_HAS_IEEE_TF) +// Note: Since there is no explicit way to tell compiler the constant is a +// 128-bit integer, we let the constant be casted to 128-bit integer +#define significandBits 112 +#define TF_MANT_DIG (significandBits + 1) + +static __inline int rep_clz(rep_t a) { + const union { + __uint128_t ll; +#if _YUGA_BIG_ENDIAN + struct { + uint64_t high, low; + } s; +#else + struct { + uint64_t low, high; + } s; +#endif + } uu = {.ll = a}; + + uint64_t word; + uint64_t add; + + if (uu.s.high) { + word = uu.s.high; + add = 0; + } else { + word = uu.s.low; + add = 64; + } + return __builtin_clzll(word) + add; +} + +#define Word_LoMask UINT64_C(0x00000000ffffffff) +#define Word_HiMask UINT64_C(0xffffffff00000000) +#define Word_FullMask UINT64_C(0xffffffffffffffff) +#define Word_1(a) (uint64_t)((a >> 96) & Word_LoMask) +#define Word_2(a) (uint64_t)((a >> 64) & Word_LoMask) +#define Word_3(a) (uint64_t)((a >> 32) & Word_LoMask) +#define Word_4(a) (uint64_t)(a & Word_LoMask) + +// 128x128 -> 256 wide multiply for platforms that don't have such an operation; +// many 64-bit platforms have this operation, but they tend to have hardware +// floating-point, so we don't bother with a special case for them here. +static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { + + const uint64_t product11 = Word_1(a) * Word_1(b); + const uint64_t product12 = Word_1(a) * Word_2(b); + const uint64_t product13 = Word_1(a) * Word_3(b); + const uint64_t product14 = Word_1(a) * Word_4(b); + const uint64_t product21 = Word_2(a) * Word_1(b); + const uint64_t product22 = Word_2(a) * Word_2(b); + const uint64_t product23 = Word_2(a) * Word_3(b); + const uint64_t product24 = Word_2(a) * Word_4(b); + const uint64_t product31 = Word_3(a) * Word_1(b); + const uint64_t product32 = Word_3(a) * Word_2(b); + const uint64_t product33 = Word_3(a) * Word_3(b); + const uint64_t product34 = Word_3(a) * Word_4(b); + const uint64_t product41 = Word_4(a) * Word_1(b); + const uint64_t product42 = Word_4(a) * Word_2(b); + const uint64_t product43 = Word_4(a) * Word_3(b); + const uint64_t product44 = Word_4(a) * Word_4(b); + + const __uint128_t sum0 = (__uint128_t)product44; + const __uint128_t sum1 = (__uint128_t)product34 + (__uint128_t)product43; + const __uint128_t sum2 = + (__uint128_t)product24 + (__uint128_t)product33 + (__uint128_t)product42; + const __uint128_t sum3 = (__uint128_t)product14 + (__uint128_t)product23 + + (__uint128_t)product32 + (__uint128_t)product41; + const __uint128_t sum4 = + (__uint128_t)product13 + (__uint128_t)product22 + (__uint128_t)product31; + const __uint128_t sum5 = (__uint128_t)product12 + (__uint128_t)product21; + const __uint128_t sum6 = (__uint128_t)product11; + + const __uint128_t r0 = (sum0 & Word_FullMask) + ((sum1 & Word_LoMask) << 32); + const __uint128_t r1 = (sum0 >> 64) + ((sum1 >> 32) & Word_FullMask) + + (sum2 & Word_FullMask) + ((sum3 << 32) & Word_HiMask); + + *lo = r0 + (r1 << 64); + *hi = (r1 >> 64) + (sum1 >> 96) + (sum2 >> 64) + (sum3 >> 32) + sum4 + + (sum5 << 32) + (sum6 << 64); +} +#undef Word_1 +#undef Word_2 +#undef Word_3 +#undef Word_4 +#undef Word_HiMask +#undef Word_LoMask +#undef Word_FullMask +#endif // defined(CRT_HAS_IEEE_TF) +#else +typedef long double fp_t; +#endif // defined(CRT_HAS_F128) && defined(CRT_HAS_128BIT) +#else +#error SINGLE_PRECISION, DOUBLE_PRECISION or QUAD_PRECISION must be defined. +#endif + +#if defined(SINGLE_PRECISION) || defined(DOUBLE_PRECISION) || \ + (defined(QUAD_PRECISION) && defined(CRT_HAS_TF_MODE)) +#define typeWidth (sizeof(rep_t) * CHAR_BIT) + +static __inline rep_t toRep(fp_t x) { + const union { + fp_t f; + rep_t i; + } rep = {.f = x}; + return rep.i; +} + +static __inline fp_t fromRep(rep_t x) { + const union { + fp_t f; + rep_t i; + } rep = {.i = x}; + return rep.f; +} + +#if !defined(QUAD_PRECISION) || defined(CRT_HAS_IEEE_TF) +#define exponentBits (typeWidth - significandBits - 1) +#define maxExponent ((1 << exponentBits) - 1) +#define exponentBias (maxExponent >> 1) + +#define implicitBit (REP_C(1) << significandBits) +#define significandMask (implicitBit - 1U) +#define signBit (REP_C(1) << (significandBits + exponentBits)) +#define absMask (signBit - 1U) +#define exponentMask (absMask ^ significandMask) +#define oneRep ((rep_t)exponentBias << significandBits) +#define infRep exponentMask +#define quietBit (implicitBit >> 1) +#define qnanRep (exponentMask | quietBit) + +static __inline int normalize(rep_t *significand) { + const int shift = rep_clz(*significand) - rep_clz(implicitBit); + *significand <<= shift; + return 1 - shift; +} + +static __inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) { + *hi = *hi << count | *lo >> (typeWidth - count); + *lo = *lo << count; +} + +static __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, + unsigned int count) { + if (count < typeWidth) { + const bool sticky = (*lo << (typeWidth - count)) != 0; + *lo = *hi << (typeWidth - count) | *lo >> count | sticky; + *hi = *hi >> count; + } else if (count < 2 * typeWidth) { + const bool sticky = *hi << (2 * typeWidth - count) | *lo; + *lo = *hi >> (count - typeWidth) | sticky; + *hi = 0; + } else { + const bool sticky = *hi | *lo; + *lo = sticky; + *hi = 0; + } +} + +// Implements logb methods (logb, logbf, logbl) for IEEE-754. This avoids +// pulling in a libm dependency from compiler-rt, but is not meant to replace +// it (i.e. code calling logb() should get the one from libm, not this), hence +// the __compiler_rt prefix. +static __inline fp_t __compiler_rt_logbX(fp_t x) { + rep_t rep = toRep(x); + int exp = (rep & exponentMask) >> significandBits; + + // Abnormal cases: + // 1) +/- inf returns +inf; NaN returns NaN + // 2) 0.0 returns -inf + if (exp == maxExponent) { + if (((rep & signBit) == 0) || (x != x)) { + return x; // NaN or +inf: return x + } else { + return -x; // -inf: return -x + } + } else if (x == 0.0) { + // 0.0: return -inf + return fromRep(infRep | signBit); + } + + if (exp != 0) { + // Normal number + return exp - exponentBias; // Unbias exponent + } else { + // Subnormal number; normalize and repeat + rep &= absMask; + const int shift = 1 - normalize(&rep); + exp = (rep & exponentMask) >> significandBits; + return exp - exponentBias - shift; // Unbias exponent + } +} + +// Avoid using scalbn from libm. Unlike libc/libm scalbn, this function never +// sets errno on underflow/overflow. +static __inline fp_t __compiler_rt_scalbnX(fp_t x, int y) { + const rep_t rep = toRep(x); + int exp = (rep & exponentMask) >> significandBits; + + if (x == 0.0 || exp == maxExponent) + return x; // +/- 0.0, NaN, or inf: return x + + // Normalize subnormal input. + rep_t sig = rep & significandMask; + if (exp == 0) { + exp += normalize(&sig); + sig &= ~implicitBit; // clear the implicit bit again + } + + if (__builtin_sadd_overflow(exp, y, &exp)) { + // Saturate the exponent, which will guarantee an underflow/overflow below. + exp = (y >= 0) ? INT_MAX : INT_MIN; + } + + // Return this value: [+/-] 1.sig * 2 ** (exp - exponentBias). + const rep_t sign = rep & signBit; + if (exp >= maxExponent) { + // Overflow, which could produce infinity or the largest-magnitude value, + // depending on the rounding mode. + return fromRep(sign | ((rep_t)(maxExponent - 1) << significandBits)) * 2.0f; + } else if (exp <= 0) { + // Subnormal or underflow. Use floating-point multiply to handle truncation + // correctly. + fp_t tmp = fromRep(sign | (REP_C(1) << significandBits) | sig); + exp += exponentBias - 1; + if (exp < 1) + exp = 1; + tmp *= fromRep((rep_t)exp << significandBits); + return tmp; + } else + return fromRep(sign | ((rep_t)exp << significandBits) | sig); +} + +#endif // !defined(QUAD_PRECISION) || defined(CRT_HAS_IEEE_TF) + +// Avoid using fmax from libm. +static __inline fp_t __compiler_rt_fmaxX(fp_t x, fp_t y) { + // If either argument is NaN, return the other argument. If both are NaN, + // arbitrarily return the second one. Otherwise, if both arguments are +/-0, + // arbitrarily return the first one. + return (crt_isnan(x) || x < y) ? y : x; +} + +#endif + +#if defined(SINGLE_PRECISION) + +static __inline fp_t __compiler_rt_logbf(fp_t x) { + return __compiler_rt_logbX(x); +} +static __inline fp_t __compiler_rt_scalbnf(fp_t x, int y) { + return __compiler_rt_scalbnX(x, y); +} +static __inline fp_t __compiler_rt_fmaxf(fp_t x, fp_t y) { +#if defined(__aarch64__) + // Use __builtin_fmaxf which turns into an fmaxnm instruction on AArch64. + return __builtin_fmaxf(x, y); +#else + // __builtin_fmaxf frequently turns into a libm call, so inline the function. + return __compiler_rt_fmaxX(x, y); +#endif +} + +#elif defined(DOUBLE_PRECISION) + +static __inline fp_t __compiler_rt_logb(fp_t x) { + return __compiler_rt_logbX(x); +} +static __inline fp_t __compiler_rt_scalbn(fp_t x, int y) { + return __compiler_rt_scalbnX(x, y); +} +static __inline fp_t __compiler_rt_fmax(fp_t x, fp_t y) { +#if defined(__aarch64__) + // Use __builtin_fmax which turns into an fmaxnm instruction on AArch64. + return __builtin_fmax(x, y); +#else + // __builtin_fmax frequently turns into a libm call, so inline the function. + return __compiler_rt_fmaxX(x, y); +#endif +} + +#elif defined(QUAD_PRECISION) && defined(CRT_HAS_TF_MODE) +// The generic implementation only works for ieee754 floating point. For other +// floating point types, continue to rely on the libm implementation for now. +#if defined(CRT_HAS_IEEE_TF) +static __inline tf_float __compiler_rt_logbtf(tf_float x) { + return __compiler_rt_logbX(x); +} +static __inline tf_float __compiler_rt_scalbntf(tf_float x, int y) { + return __compiler_rt_scalbnX(x, y); +} +static __inline tf_float __compiler_rt_fmaxtf(tf_float x, tf_float y) { + return __compiler_rt_fmaxX(x, y); +} +#define __compiler_rt_logbl __compiler_rt_logbtf +#define __compiler_rt_scalbnl __compiler_rt_scalbntf +#define __compiler_rt_fmaxl __compiler_rt_fmaxtf +#define crt_fabstf crt_fabsf128 +#define crt_copysigntf crt_copysignf128 +#elif defined(CRT_LDBL_128BIT) +static __inline tf_float __compiler_rt_logbtf(tf_float x) { + return crt_logbl(x); +} +static __inline tf_float __compiler_rt_scalbntf(tf_float x, int y) { + return crt_scalbnl(x, y); +} +static __inline tf_float __compiler_rt_fmaxtf(tf_float x, tf_float y) { + return crt_fmaxl(x, y); +} +#define __compiler_rt_logbl crt_logbl +#define __compiler_rt_scalbnl crt_scalbnl +#define __compiler_rt_fmaxl crt_fmaxl +#define crt_fabstf crt_fabsl +#define crt_copysigntf crt_copysignl +#else +#error Unsupported TF mode type +#endif + +#endif // *_PRECISION + +#endif // FP_LIB_HEADER diff --git a/src/bflat/modules/softfloat/builtins/fp_mode.c b/src/bflat/modules/softfloat/builtins/fp_mode.c new file mode 100644 index 0000000..5186547 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_mode.c @@ -0,0 +1,22 @@ +//===----- lib/fp_mode.c - Floaing-point environment mode utilities --C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides a default implementation of fp_mode.h for architectures +// that does not support or does not have an implementation of floating point +// environment mode. +// +//===----------------------------------------------------------------------===// + +#include "fp_mode.h" + +// IEEE-754 default rounding (to nearest, ties to even). +CRT_FE_ROUND_MODE __fe_getround(void) { return CRT_FE_TONEAREST; } + +int __fe_raise_inexact(void) { + return 0; +} diff --git a/src/bflat/modules/softfloat/builtins/fp_mode.h b/src/bflat/modules/softfloat/builtins/fp_mode.h new file mode 100644 index 0000000..5b4969a --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_mode.h @@ -0,0 +1,29 @@ +//===----- lib/fp_mode.h - Floaing-point environment mode utilities --C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is not part of the interface of this library. +// +// This file defines an interface for accessing hardware floating point +// environment mode. +// +//===----------------------------------------------------------------------===// + +#ifndef FP_MODE_H +#define FP_MODE_H + +typedef enum { + CRT_FE_TONEAREST, + CRT_FE_DOWNWARD, + CRT_FE_UPWARD, + CRT_FE_TOWARDZERO +} CRT_FE_ROUND_MODE; + +CRT_FE_ROUND_MODE __fe_getround(void); +int __fe_raise_inexact(void); + +#endif // FP_MODE_H diff --git a/src/bflat/modules/softfloat/builtins/fp_mul_impl.inc b/src/bflat/modules/softfloat/builtins/fp_mul_impl.inc new file mode 100644 index 0000000..a93f2d7 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_mul_impl.inc @@ -0,0 +1,128 @@ +//===---- lib/fp_mul_impl.inc - floating point multiplication -----*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements soft-float multiplication with the IEEE-754 default +// rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#include "fp_lib.h" + +static __inline fp_t __mulXf3__(fp_t a, fp_t b) { + const unsigned int aExponent = toRep(a) >> significandBits & maxExponent; + const unsigned int bExponent = toRep(b) >> significandBits & maxExponent; + const rep_t productSign = (toRep(a) ^ toRep(b)) & signBit; + + rep_t aSignificand = toRep(a) & significandMask; + rep_t bSignificand = toRep(b) & significandMask; + int scale = 0; + + // Detect if a or b is zero, denormal, infinity, or NaN. + if (aExponent - 1U >= maxExponent - 1U || + bExponent - 1U >= maxExponent - 1U) { + + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + + // NaN * anything = qNaN + if (aAbs > infRep) + return fromRep(toRep(a) | quietBit); + // anything * NaN = qNaN + if (bAbs > infRep) + return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // infinity * non-zero = +/- infinity + if (bAbs) + return fromRep(aAbs | productSign); + // infinity * zero = NaN + else + return fromRep(qnanRep); + } + + if (bAbs == infRep) { + // non-zero * infinity = +/- infinity + if (aAbs) + return fromRep(bAbs | productSign); + // zero * infinity = NaN + else + return fromRep(qnanRep); + } + + // zero * anything = +/- zero + if (!aAbs) + return fromRep(productSign); + // anything * zero = +/- zero + if (!bAbs) + return fromRep(productSign); + + // One or both of a or b is denormal. The other (if applicable) is a + // normal number. Renormalize one or both of a and b, and set scale to + // include the necessary exponent adjustment. + if (aAbs < implicitBit) + scale += normalize(&aSignificand); + if (bAbs < implicitBit) + scale += normalize(&bSignificand); + } + + // Set the implicit significand bit. If we fell through from the + // denormal path it was already set by normalize( ), but setting it twice + // won't hurt anything. + aSignificand |= implicitBit; + bSignificand |= implicitBit; + + // Perform a basic multiplication on the significands. One of them must be + // shifted beforehand to be aligned with the exponent. + rep_t productHi, productLo; + wideMultiply(aSignificand, bSignificand << exponentBits, &productHi, + &productLo); + + int productExponent = aExponent + bExponent - exponentBias + scale; + + // Normalize the significand and adjust the exponent if needed. + if (productHi & implicitBit) + productExponent++; + else + wideLeftShift(&productHi, &productLo, 1); + + // If we have overflowed the type, return +/- infinity. + if (productExponent >= maxExponent) + return fromRep(infRep | productSign); + + if (productExponent <= 0) { + // The result is denormal before rounding. + // + // If the result is so small that it just underflows to zero, return + // zero with the appropriate sign. Mathematically, there is no need to + // handle this case separately, but we make it a special case to + // simplify the shift logic. + const unsigned int shift = REP_C(1) - (unsigned int)productExponent; + if (shift >= typeWidth) + return fromRep(productSign); + + // Otherwise, shift the significand of the result so that the round + // bit is the high bit of productLo. + wideRightShiftWithSticky(&productHi, &productLo, shift); + } else { + // The result is normal before rounding. Insert the exponent. + productHi &= significandMask; + productHi |= (rep_t)productExponent << significandBits; + } + + // Insert the sign of the result. + productHi |= productSign; + + // Perform the final rounding. The final result may overflow to infinity, + // or underflow to zero, but those are the correct results in those cases. + // We use the default IEEE-754 round-to-nearest, ties-to-even rounding mode. + if (productLo > signBit) + productHi++; + if (productLo == signBit) + productHi += productHi & 1; + return fromRep(productHi); +} diff --git a/src/bflat/modules/softfloat/builtins/fp_trunc.h b/src/bflat/modules/softfloat/builtins/fp_trunc.h new file mode 100644 index 0000000..141fe63 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_trunc.h @@ -0,0 +1,158 @@ +//=== lib/fp_trunc.h - high precision -> low precision conversion *- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Set source and destination precision setting +// +//===----------------------------------------------------------------------===// + +#ifndef FP_TRUNC_HEADER +#define FP_TRUNC_HEADER + +#include "int_lib.h" + +#if defined SRC_SINGLE +typedef float src_t; +typedef uint32_t src_rep_t; +#define SRC_REP_C UINT32_C +static const int srcBits = sizeof(src_t) * CHAR_BIT; +static const int srcSigFracBits = 23; +// -1 accounts for the sign bit. +// srcBits - srcSigFracBits - 1 +static const int srcExpBits = 8; + +#elif defined SRC_DOUBLE +typedef double src_t; +typedef uint64_t src_rep_t; +#define SRC_REP_C UINT64_C +static const int srcBits = sizeof(src_t) * CHAR_BIT; +static const int srcSigFracBits = 52; +// -1 accounts for the sign bit. +// srcBits - srcSigFracBits - 1 +static const int srcExpBits = 11; + +#elif defined SRC_QUAD +typedef tf_float src_t; +typedef __uint128_t src_rep_t; +#define SRC_REP_C (__uint128_t) +static const int srcBits = sizeof(src_t) * CHAR_BIT; +static const int srcSigFracBits = 112; +// -1 accounts for the sign bit. +// srcBits - srcSigFracBits - 1 +static const int srcExpBits = 15; + +#else +#error Source should be double precision or quad precision! +#endif // end source precision + +#if defined DST_DOUBLE +typedef double dst_t; +typedef uint64_t dst_rep_t; +#define DST_REP_C UINT64_C +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 52; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 11; + +#elif defined DST_80 +typedef xf_float dst_t; +typedef __uint128_t dst_rep_t; +#define DST_REP_C (__uint128_t) +static const int dstBits = 80; +static const int dstSigFracBits = 63; +// -1 accounts for the sign bit. +// -1 accounts for the explicitly stored integer bit. +// dstBits - dstSigFracBits - 1 - 1 +static const int dstExpBits = 15; + +#elif defined DST_SINGLE +typedef float dst_t; +typedef uint32_t dst_rep_t; +#define DST_REP_C UINT32_C +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 23; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 8; + +#elif defined DST_HALF +#ifdef COMPILER_RT_HAS_FLOAT16 +typedef _Float16 dst_t; +#else +typedef uint16_t dst_t; +#endif +typedef uint16_t dst_rep_t; +#define DST_REP_C UINT16_C +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 10; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 5; + +#elif defined DST_BFLOAT +typedef __bf16 dst_t; +typedef uint16_t dst_rep_t; +#define DST_REP_C UINT16_C +static const int dstBits = sizeof(dst_t) * CHAR_BIT; +static const int dstSigFracBits = 7; +// -1 accounts for the sign bit. +// dstBits - dstSigFracBits - 1 +static const int dstExpBits = 8; + +#else +#error Destination should be single precision or double precision! +#endif // end destination precision + +// TODO: These helper routines should be placed into fp_lib.h +// Currently they depend on macros/constants defined above. + +static inline src_rep_t extract_sign_from_src(src_rep_t x) { + const src_rep_t srcSignMask = SRC_REP_C(1) << (srcBits - 1); + return (x & srcSignMask) >> (srcBits - 1); +} + +static inline src_rep_t extract_exp_from_src(src_rep_t x) { + const int srcSigBits = srcBits - 1 - srcExpBits; + const src_rep_t srcExpMask = ((SRC_REP_C(1) << srcExpBits) - 1) << srcSigBits; + return (x & srcExpMask) >> srcSigBits; +} + +static inline src_rep_t extract_sig_frac_from_src(src_rep_t x) { + const src_rep_t srcSigFracMask = (SRC_REP_C(1) << srcSigFracBits) - 1; + return x & srcSigFracMask; +} + +static inline dst_rep_t construct_dst_rep(dst_rep_t sign, dst_rep_t exp, dst_rep_t sigFrac) { + dst_rep_t result = (sign << (dstBits - 1)) | (exp << (dstBits - 1 - dstExpBits)) | sigFrac; + // Set the explicit integer bit in F80 if present. + if (dstBits == 80 && exp) { + result |= (DST_REP_C(1) << dstSigFracBits); + } + return result; +} + +// End of specialization parameters. Two helper routines for conversion to and +// from the representation of floating-point data as integer values follow. + +static inline src_rep_t srcToRep(src_t x) { + const union { + src_t f; + src_rep_t i; + } rep = {.f = x}; + return rep.i; +} + +static inline dst_t dstFromRep(dst_rep_t x) { + const union { + dst_t f; + dst_rep_t i; + } rep = {.i = x}; + return rep.f; +} + +#endif // FP_TRUNC_HEADER diff --git a/src/bflat/modules/softfloat/builtins/fp_trunc_impl.inc b/src/bflat/modules/softfloat/builtins/fp_trunc_impl.inc new file mode 100644 index 0000000..f684924 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/fp_trunc_impl.inc @@ -0,0 +1,155 @@ +//= lib/fp_trunc_impl.inc - high precision -> low precision conversion *-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a fairly generic conversion from a wider to a narrower +// IEEE-754 floating-point type in the default (round to nearest, ties to even) +// rounding mode. The constants and types defined following the includes below +// parameterize the conversion. +// +// This routine can be trivially adapted to support conversions to +// half-precision or from quad-precision. It does not support types that don't +// use the usual IEEE-754 interchange formats; specifically, some work would be +// needed to adapt it to (for example) the Intel 80-bit format or PowerPC +// double-double format. +// +// Note please, however, that this implementation is only intended to support +// *narrowing* operations; if you need to convert to a *wider* floating-point +// type (e.g. float -> double), then this routine will not do what you want it +// to. +// +// It also requires that integer types at least as large as both formats +// are available on the target platform; this may pose a problem when trying +// to add support for quad on some 32-bit systems, for example. +// +// Finally, the following assumptions are made: +// +// 1. Floating-point types and integer types have the same endianness on the +// target platform. +// +// 2. Quiet NaNs, if supported, are indicated by the leading bit of the +// significand field being set. +// +//===----------------------------------------------------------------------===// + +#include "fp_trunc.h" + +// The destination type may use a usual IEEE-754 interchange format or Intel +// 80-bit format. In particular, for the destination type dstSigFracBits may be +// not equal to dstSigBits. The source type is assumed to be one of IEEE-754 +// standard types. +static __inline dst_t __truncXfYf2__(src_t a) { + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const int srcInfExp = (1 << srcExpBits) - 1; + const int srcExpBias = srcInfExp >> 1; + + const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigFracBits; + const src_rep_t roundMask = + (SRC_REP_C(1) << (srcSigFracBits - dstSigFracBits)) - 1; + const src_rep_t halfway = SRC_REP_C(1) + << (srcSigFracBits - dstSigFracBits - 1); + const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigFracBits - 1); + const src_rep_t srcNaNCode = srcQNaN - 1; + + const int dstInfExp = (1 << dstExpBits) - 1; + const int dstExpBias = dstInfExp >> 1; + const int overflowExponent = srcExpBias + dstInfExp - dstExpBias; + + const dst_rep_t dstQNaN = DST_REP_C(1) << (dstSigFracBits - 1); + const dst_rep_t dstNaNCode = dstQNaN - 1; + + const src_rep_t aRep = srcToRep(a); + const src_rep_t srcSign = extract_sign_from_src(aRep); + const src_rep_t srcExp = extract_exp_from_src(aRep); + const src_rep_t srcSigFrac = extract_sig_frac_from_src(aRep); + + dst_rep_t dstSign = srcSign; + dst_rep_t dstExp; + dst_rep_t dstSigFrac; + + // Same size exponents and a's significand tail is 0. + // The significand can be truncated and the exponent can be copied over. + const int sigFracTailBits = srcSigFracBits - dstSigFracBits; + if (srcExpBits == dstExpBits && + ((aRep >> sigFracTailBits) << sigFracTailBits) == aRep) { + dstExp = srcExp; + dstSigFrac = (dst_rep_t)(srcSigFrac >> sigFracTailBits); + return dstFromRep(construct_dst_rep(dstSign, dstExp, dstSigFrac)); + } + + const int dstExpCandidate = ((int)srcExp - srcExpBias) + dstExpBias; + if (dstExpCandidate >= 1 && dstExpCandidate < dstInfExp) { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + dstExp = dstExpCandidate; + dstSigFrac = (dst_rep_t)(srcSigFrac >> sigFracTailBits); + + const src_rep_t roundBits = srcSigFrac & roundMask; + // Round to nearest. + if (roundBits > halfway) + dstSigFrac++; + // Tie to even. + else if (roundBits == halfway) + dstSigFrac += dstSigFrac & 1; + + // Rounding has changed the exponent. + if (dstSigFrac >= (DST_REP_C(1) << dstSigFracBits)) { + dstExp += 1; + dstSigFrac ^= (DST_REP_C(1) << dstSigFracBits); + } + } else if (srcExp == srcInfExp && srcSigFrac) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + dstExp = dstInfExp; + dstSigFrac = dstQNaN; + dstSigFrac |= ((srcSigFrac & srcNaNCode) >> sigFracTailBits) & dstNaNCode; + } else if ((int)srcExp >= overflowExponent) { + dstExp = dstInfExp; + dstSigFrac = 0; + } else { + // a underflows on conversion to the destination type or is an exact + // zero. The result may be a denormal or zero. Extract the exponent + // to get the shift amount for the denormalization. + src_rep_t significand = srcSigFrac; + int shift = srcExpBias - dstExpBias - srcExp; + + if (srcExp) { + // Set the implicit integer bit if the source is a normal number. + significand |= srcMinNormal; + shift += 1; + } + + // Right shift by the denormalization amount with sticky. + if (shift > srcSigFracBits) { + dstExp = 0; + dstSigFrac = 0; + } else { + dstExp = 0; + const bool sticky = shift && ((significand << (srcBits - shift)) != 0); + src_rep_t denormalizedSignificand = significand >> shift | sticky; + dstSigFrac = denormalizedSignificand >> sigFracTailBits; + const src_rep_t roundBits = denormalizedSignificand & roundMask; + // Round to nearest + if (roundBits > halfway) + dstSigFrac++; + // Ties to even + else if (roundBits == halfway) + dstSigFrac += dstSigFrac & 1; + + // Rounding has changed the exponent. + if (dstSigFrac >= (DST_REP_C(1) << dstSigFracBits)) { + dstExp += 1; + dstSigFrac ^= (DST_REP_C(1) << dstSigFracBits); + } + } + } + + return dstFromRep(construct_dst_rep(dstSign, dstExp, dstSigFrac)); +} diff --git a/src/bflat/modules/softfloat/builtins/int_endianness.h b/src/bflat/modules/softfloat/builtins/int_endianness.h new file mode 100644 index 0000000..291c6b5 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_endianness.h @@ -0,0 +1,114 @@ +//===-- int_endianness.h - configuration header for compiler-rt -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a configuration header for compiler-rt. +// This file is not part of the interface of this library. +// +//===----------------------------------------------------------------------===// + +#ifndef INT_ENDIANNESS_H +#define INT_ENDIANNESS_H + +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + defined(__ORDER_LITTLE_ENDIAN__) + +// Clang and GCC provide built-in endianness definitions. +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif // __BYTE_ORDER__ + +#else // Compilers other than Clang or GCC. + +#if defined(__SVR4) && defined(__sun) +#include + +#if defined(_BIG_ENDIAN) +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif defined(_LITTLE_ENDIAN) +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#else // !_LITTLE_ENDIAN +#error "unknown endianness" +#endif // !_LITTLE_ENDIAN + +#endif // Solaris + +// .. + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + defined(__minix) +#include + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif // _BYTE_ORDER + +#endif // *BSD + +#if defined(__OpenBSD__) +#include + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif // _BYTE_ORDER + +#endif // OpenBSD + +// .. + +// Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the +// compiler (at least with GCC) +#if defined(__APPLE__) || defined(__ellcc__) + +#ifdef __BIG_ENDIAN__ +#if __BIG_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#endif +#endif // __BIG_ENDIAN__ + +#ifdef __LITTLE_ENDIAN__ +#if __LITTLE_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif +#endif // __LITTLE_ENDIAN__ + +#endif // Mac OSX + +// .. + +#if defined(_WIN32) + +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 + +#endif // Windows + +#endif // Clang or GCC. + +// . + +#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN) +#error Unable to determine endian +#endif // Check we found an endianness correctly. + +#endif // INT_ENDIANNESS_H diff --git a/src/bflat/modules/softfloat/builtins/int_lib.h b/src/bflat/modules/softfloat/builtins/int_lib.h new file mode 100644 index 0000000..f6c1b7c --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_lib.h @@ -0,0 +1,171 @@ +//===-- int_lib.h - configuration header for compiler-rt -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a configuration header for compiler-rt. +// This file is not part of the interface of this library. +// +//===----------------------------------------------------------------------===// + +#ifndef INT_LIB_H +#define INT_LIB_H + +// Assumption: Signed integral is 2's complement. +// Assumption: Right shift of signed negative is arithmetic shift. +// Assumption: Endianness is little or big (not mixed). + +// ABI macro definitions + +#if __ARM_EABI__ +#ifdef COMPILER_RT_ARMHF_TARGET +#define COMPILER_RT_ABI +#else +#define COMPILER_RT_ABI __attribute__((__pcs__("aapcs"))) +#endif +#else +#define COMPILER_RT_ABI +#endif + +#define AEABI_RTABI __attribute__((__pcs__("aapcs"))) + +#if defined(_MSC_VER) && !defined(__clang__) +#define ALWAYS_INLINE __forceinline +#define NOINLINE __declspec(noinline) +#define NORETURN __declspec(noreturn) +#define UNUSED +#else +#define ALWAYS_INLINE __attribute__((always_inline)) +#define NOINLINE __attribute__((noinline)) +#define NORETURN __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) +#endif + +#define STR(a) #a +#define XSTR(a) STR(a) +#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name + +#if defined(__ELF__) || defined(__MINGW32__) || defined(__wasm__) || \ + defined(_AIX) || defined(__CYGWIN__) +#define COMPILER_RT_ALIAS(name, aliasname) \ + COMPILER_RT_ABI __typeof(name) aliasname __attribute__((__alias__(#name))); +#elif defined(__APPLE__) +#if defined(VISIBILITY_HIDDEN) +#define COMPILER_RT_ALIAS_VISIBILITY(name) \ + __asm__(".private_extern " SYMBOL_NAME(name)); +#else +#define COMPILER_RT_ALIAS_VISIBILITY(name) +#endif +#define COMPILER_RT_ALIAS(name, aliasname) \ + __asm__(".globl " SYMBOL_NAME(aliasname)); \ + COMPILER_RT_ALIAS_VISIBILITY(aliasname) \ + __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \ + COMPILER_RT_ABI __typeof(name) aliasname; +#elif defined(_WIN32) +#define COMPILER_RT_ALIAS(name, aliasname) +#else +#error Unsupported target +#endif + +#if (defined(__FreeBSD__) || defined(__NetBSD__)) && \ + (defined(_KERNEL) || defined(_STANDALONE)) +// +// Kernel and boot environment can't use normal headers, +// so use the equivalent system headers. +// NB: FreeBSD (and OpenBSD) deprecate machine/limits.h in +// favour of sys/limits.h, so prefer the former, but fall +// back on the latter if not available since NetBSD only has +// the latter. +// +#if defined(__has_include) && __has_include() +#include +#else +#include +#endif +#include +#include +#else +// Include the standard compiler builtin headers we use functionality from. +#include +#include +#include +#include +#endif + +// Include the commonly used internal type definitions. +#include "int_types.h" + +// Include internal utility function declarations. +#include "int_util.h" + +COMPILER_RT_ABI int __paritysi2(si_int a); +COMPILER_RT_ABI int __paritydi2(di_int a); + +COMPILER_RT_ABI di_int __divdi3(di_int a, di_int b); +COMPILER_RT_ABI si_int __divsi3(si_int a, si_int b); +COMPILER_RT_ABI su_int __udivsi3(su_int n, su_int d); + +COMPILER_RT_ABI su_int __udivmodsi4(su_int a, su_int b, su_int *rem); +COMPILER_RT_ABI du_int __udivmoddi4(du_int a, du_int b, du_int *rem); +#ifdef CRT_HAS_128BIT +COMPILER_RT_ABI int __clzti2(ti_int a); +COMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int *rem); +#endif + +// Definitions for builtins unavailable on MSVC +#if defined(_MSC_VER) && !defined(__clang__) +#include + +static int __inline __builtin_ctz(uint32_t value) { + unsigned long trailing_zero = 0; + if (_BitScanForward(&trailing_zero, value)) + return trailing_zero; + return 32; +} + +static int __inline __builtin_clz(uint32_t value) { + unsigned long leading_zero = 0; + if (_BitScanReverse(&leading_zero, value)) + return 31 - leading_zero; + return 32; +} + +#if defined(_M_ARM) || defined(_M_X64) +static int __inline __builtin_clzll(uint64_t value) { + unsigned long leading_zero = 0; + if (_BitScanReverse64(&leading_zero, value)) + return 63 - leading_zero; + return 64; +} +#else +static int __inline __builtin_clzll(uint64_t value) { + if (value == 0) + return 64; + uint32_t msh = (uint32_t)(value >> 32); + uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); + if (msh != 0) + return __builtin_clz(msh); + return 32 + __builtin_clz(lsh); +} +#endif + +#define __builtin_clzl __builtin_clzll + +static bool __inline __builtin_sadd_overflow(int x, int y, int *result) { + if ((x < 0) != (y < 0)) { + *result = x + y; + return false; + } + int tmp = (unsigned int)x + (unsigned int)y; + if ((tmp < 0) != (x < 0)) + return true; + *result = tmp; + return false; +} + +#endif // defined(_MSC_VER) && !defined(__clang__) + +#endif // INT_LIB_H diff --git a/src/bflat/modules/softfloat/builtins/int_math.h b/src/bflat/modules/softfloat/builtins/int_math.h new file mode 100644 index 0000000..74d3e31 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_math.h @@ -0,0 +1,108 @@ +//===-- int_math.h - internal math inlines --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is not part of the interface of this library. +// +// This file defines substitutes for the libm functions used in some of the +// compiler-rt implementations, defined in such a way that there is not a direct +// dependency on libm or math.h. Instead, we use the compiler builtin versions +// where available. This reduces our dependencies on the system SDK by foisting +// the responsibility onto the compiler. +// +//===----------------------------------------------------------------------===// + +#ifndef INT_MATH_H +#define INT_MATH_H + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#include +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define CRT_INFINITY INFINITY +#else +#define CRT_INFINITY __builtin_huge_valf() +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_isfinite(x) _finite((x)) +#define crt_isinf(x) !_finite((x)) +#define crt_isnan(x) _isnan((x)) +#else +// Define crt_isfinite in terms of the builtin if available, otherwise provide +// an alternate version in terms of our other functions. This supports some +// versions of GCC which didn't have __builtin_isfinite. +#if __has_builtin(__builtin_isfinite) +#define crt_isfinite(x) __builtin_isfinite((x)) +#elif defined(__GNUC__) +#define crt_isfinite(x) \ + __extension__(({ \ + __typeof((x)) x_ = (x); \ + !crt_isinf(x_) && !crt_isnan(x_); \ + })) +#else +#error "Do not know how to check for infinity" +#endif // __has_builtin(__builtin_isfinite) +#define crt_isinf(x) __builtin_isinf((x)) +#define crt_isnan(x) __builtin_isnan((x)) +#endif // _MSC_VER + +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_copysign(x, y) copysign((x), (y)) +#define crt_copysignf(x, y) copysignf((x), (y)) +#define crt_copysignl(x, y) copysignl((x), (y)) +#else +#define crt_copysign(x, y) __builtin_copysign((x), (y)) +#define crt_copysignf(x, y) __builtin_copysignf((x), (y)) +#define crt_copysignl(x, y) __builtin_copysignl((x), (y)) +#if __has_builtin(__builtin_copysignf128) +#define crt_copysignf128(x, y) __builtin_copysignf128((x), (y)) +#elif __has_builtin(__builtin_copysignq) || (defined(__GNUC__) && __GNUC__ >= 7) +#define crt_copysignf128(x, y) __builtin_copysignq((x), (y)) +#endif +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_fabs(x) fabs((x)) +#define crt_fabsf(x) fabsf((x)) +#define crt_fabsl(x) fabs((x)) +#else +#define crt_fabs(x) __builtin_fabs((x)) +#define crt_fabsf(x) __builtin_fabsf((x)) +#define crt_fabsl(x) __builtin_fabsl((x)) +#if __has_builtin(__builtin_fabsf128) +#define crt_fabsf128(x) __builtin_fabsf128((x)) +#elif __has_builtin(__builtin_fabsq) || (defined(__GNUC__) && __GNUC__ >= 7) +#define crt_fabsf128(x) __builtin_fabsq((x)) +#endif +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_fmaxl(x, y) __max((x), (y)) +#else +#define crt_fmaxl(x, y) __builtin_fmaxl((x), (y)) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_logbl(x) logbl((x)) +#else +#define crt_logbl(x) __builtin_logbl((x)) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_scalbnl(x, y) scalbnl((x), (y)) +#else +#define crt_scalbnl(x, y) __builtin_scalbnl((x), (y)) +#endif + +#endif // INT_MATH_H diff --git a/src/bflat/modules/softfloat/builtins/int_to_fp.h b/src/bflat/modules/softfloat/builtins/int_to_fp.h new file mode 100644 index 0000000..2c1218f --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_to_fp.h @@ -0,0 +1,82 @@ +//===-- int_to_fp.h - integer to floating point conversion ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Set source and destination defines in order to use a correctly +// parameterised floatXiYf implementation. +// +//===----------------------------------------------------------------------===// + +#ifndef INT_TO_FP_H +#define INT_TO_FP_H + +#include "int_lib.h" + +#if defined SRC_I64 +typedef int64_t src_t; +typedef uint64_t usrc_t; +static __inline int clzSrcT(usrc_t x) { return __builtin_clzll(x); } + +#elif defined SRC_U64 +typedef uint64_t src_t; +typedef uint64_t usrc_t; +static __inline int clzSrcT(usrc_t x) { return __builtin_clzll(x); } + +#elif defined SRC_I128 +typedef __int128_t src_t; +typedef __uint128_t usrc_t; +static __inline int clzSrcT(usrc_t x) { return __clzti2(x); } + +#elif defined SRC_U128 +typedef __uint128_t src_t; +typedef __uint128_t usrc_t; +static __inline int clzSrcT(usrc_t x) { return __clzti2(x); } + +#else +#error Source should be a handled integer type. +#endif + +#if defined DST_SINGLE +typedef float dst_t; +typedef uint32_t dst_rep_t; +#define DST_REP_C UINT32_C + +enum { + dstSigBits = 23, +}; + +#elif defined DST_DOUBLE +typedef double dst_t; +typedef uint64_t dst_rep_t; +#define DST_REP_C UINT64_C + +enum { + dstSigBits = 52, +}; + +#elif defined DST_QUAD +typedef tf_float dst_t; +typedef __uint128_t dst_rep_t; +#define DST_REP_C (__uint128_t) + +enum { + dstSigBits = 112, +}; + +#else +#error Destination should be a handled floating point type +#endif + +static __inline dst_t dstFromRep(dst_rep_t x) { + const union { + dst_t f; + dst_rep_t i; + } rep = {.i = x}; + return rep.f; +} + +#endif // INT_TO_FP_H diff --git a/src/bflat/modules/softfloat/builtins/int_to_fp_impl.inc b/src/bflat/modules/softfloat/builtins/int_to_fp_impl.inc new file mode 100644 index 0000000..51f76fd --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_to_fp_impl.inc @@ -0,0 +1,72 @@ +//===-- int_to_fp_impl.inc - integer to floating point conversion ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Thsi file implements a generic conversion from an integer type to an +// IEEE-754 floating point type, allowing a common implementation to be hsared +// without copy and paste. +// +//===----------------------------------------------------------------------===// + +#include "int_to_fp.h" + +static __inline dst_t __floatXiYf__(src_t a) { + if (a == 0) + return 0.0; + + enum { + dstMantDig = dstSigBits + 1, + srcBits = sizeof(src_t) * CHAR_BIT, + srcIsSigned = ((src_t)-1) < 0, + }; + + const src_t s = srcIsSigned ? a >> (srcBits - 1) : 0; + + a = (usrc_t)(a ^ s) - s; + int sd = srcBits - clzSrcT(a); // number of significant digits + int e = sd - 1; // exponent + if (sd > dstMantDig) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit dstMantDig-1 bits to the right of 1 + // Q = bit dstMantDig bits to the right of 1 + // R = "or" of all bits to the right of Q + if (sd == dstMantDig + 1) { + a <<= 1; + } else if (sd == dstMantDig + 2) { + // Do nothing. + } else { + a = ((usrc_t)a >> (sd - (dstMantDig + 2))) | + ((a & ((usrc_t)(-1) >> ((srcBits + dstMantDig + 2) - sd))) != 0); + } + // finish: + a |= (a & 4) != 0; // Or P into R + ++a; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to dstMantDig or dstMantDig+1 bits + if (a & ((usrc_t)1 << dstMantDig)) { + a >>= 1; + ++e; + } + // a is now rounded to dstMantDig bits + } else { + a <<= (dstMantDig - sd); + // a is now rounded to dstMantDig bits + } + const int dstBits = sizeof(dst_t) * CHAR_BIT; + const dst_rep_t dstSignMask = DST_REP_C(1) << (dstBits - 1); + const int dstExpBits = dstBits - dstSigBits - 1; + const int dstExpBias = (1 << (dstExpBits - 1)) - 1; + const dst_rep_t dstSignificandMask = (DST_REP_C(1) << dstSigBits) - 1; + // Combine sign, exponent, and mantissa. + const dst_rep_t result = ((dst_rep_t)s & dstSignMask) | + ((dst_rep_t)(e + dstExpBias) << dstSigBits) | + ((dst_rep_t)(a) & dstSignificandMask); + return dstFromRep(result); +} diff --git a/src/bflat/modules/softfloat/builtins/int_types.h b/src/bflat/modules/softfloat/builtins/int_types.h new file mode 100644 index 0000000..ca97391 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_types.h @@ -0,0 +1,276 @@ +//===-- int_lib.h - configuration header for compiler-rt -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is not part of the interface of this library. +// +// This file defines various standard types, most importantly a number of unions +// used to access parts of larger types. +// +//===----------------------------------------------------------------------===// + +#ifndef INT_TYPES_H +#define INT_TYPES_H + +#include "int_endianness.h" + +// si_int is defined in Linux sysroot's asm-generic/siginfo.h +#ifdef si_int +#undef si_int +#endif +typedef int32_t si_int; +typedef uint32_t su_int; +#if UINT_MAX == 0xFFFFFFFF +#define clzsi __builtin_clz +#define ctzsi __builtin_ctz +#elif ULONG_MAX == 0xFFFFFFFF +#define clzsi __builtin_clzl +#define ctzsi __builtin_ctzl +#else +#error could not determine appropriate clzsi macro for this system +#endif + +typedef int64_t di_int; +typedef uint64_t du_int; + +typedef union { + di_int all; + struct { +#if _YUGA_LITTLE_ENDIAN + su_int low; + si_int high; +#else + si_int high; + su_int low; +#endif // _YUGA_LITTLE_ENDIAN + } s; +} dwords; + +typedef union { + du_int all; + struct { +#if _YUGA_LITTLE_ENDIAN + su_int low; + su_int high; +#else + su_int high; + su_int low; +#endif // _YUGA_LITTLE_ENDIAN + } s; +} udwords; + +#if defined(__LP64__) || defined(__wasm__) || defined(__mips64) || \ + defined(__SIZEOF_INT128__) || defined(_WIN64) +#define CRT_HAS_128BIT +#endif + +// MSVC doesn't have a working 128bit integer type. Users should really compile +// compiler-rt with clang, but if they happen to be doing a standalone build for +// asan or something else, disable the 128 bit parts so things sort of work. +#if defined(_MSC_VER) && !defined(__clang__) +#undef CRT_HAS_128BIT +#endif + +#ifdef CRT_HAS_128BIT +typedef int ti_int __attribute__((mode(TI))); +typedef unsigned tu_int __attribute__((mode(TI))); + +typedef union { + ti_int all; + struct { +#if _YUGA_LITTLE_ENDIAN + du_int low; + di_int high; +#else + di_int high; + du_int low; +#endif // _YUGA_LITTLE_ENDIAN + } s; +} twords; + +typedef union { + tu_int all; + struct { +#if _YUGA_LITTLE_ENDIAN + du_int low; + du_int high; +#else + du_int high; + du_int low; +#endif // _YUGA_LITTLE_ENDIAN + } s; +} utwords; + +static __inline ti_int make_ti(di_int h, di_int l) { + twords r; + r.s.high = h; + r.s.low = l; + return r.all; +} + +static __inline tu_int make_tu(du_int h, du_int l) { + utwords r; + r.s.high = h; + r.s.low = l; + return r.all; +} + +#endif // CRT_HAS_128BIT + +// FreeBSD's boot environment does not support using floating-point and poisons +// the float and double keywords. +#if defined(__FreeBSD__) && defined(_STANDALONE) +#define CRT_HAS_FLOATING_POINT 0 +#else +#define CRT_HAS_FLOATING_POINT 1 +#endif + +#if CRT_HAS_FLOATING_POINT +typedef union { + su_int u; + float f; +} float_bits; + +typedef union { + udwords u; + double f; +} double_bits; + +typedef struct { +#if _YUGA_LITTLE_ENDIAN + udwords low; + udwords high; +#else + udwords high; + udwords low; +#endif // _YUGA_LITTLE_ENDIAN +} uqwords; + +// Check if the target supports 80 bit extended precision long doubles. +// Notably, on x86 Windows, MSVC only provides a 64-bit long double, but GCC +// still makes it 80 bits. Clang will match whatever compiler it is trying to +// be compatible with. On 32-bit x86 Android, long double is 64 bits, while on +// x86_64 Android, long double is 128 bits. +#if (defined(__i386__) || defined(__x86_64__)) && \ + !(defined(_MSC_VER) || defined(__ANDROID__)) +#define HAS_80_BIT_LONG_DOUBLE 1 +#elif defined(__m68k__) || defined(__ia64__) +#define HAS_80_BIT_LONG_DOUBLE 1 +#else +#define HAS_80_BIT_LONG_DOUBLE 0 +#endif + +#if HAS_80_BIT_LONG_DOUBLE +typedef long double xf_float; +typedef union { + uqwords u; + xf_float f; +} xf_bits; +#endif + +#ifdef __powerpc64__ +// From https://gcc.gnu.org/wiki/Ieee128PowerPC: +// PowerPC64 uses the following suffixes: +// IFmode: IBM extended double +// KFmode: IEEE 128-bit floating point +// TFmode: Matches the default for long double. With -mabi=ieeelongdouble, +// it is IEEE 128-bit, with -mabi=ibmlongdouble IBM extended double +// Since compiler-rt only implements the tf set of libcalls, we use long double +// for the tf_float typedef. +typedef long double tf_float; +#define CRT_LDBL_128BIT +#define CRT_HAS_F128 +#if __LDBL_MANT_DIG__ == 113 && !defined(__LONG_DOUBLE_IBM128__) +#define CRT_HAS_IEEE_TF +#define CRT_LDBL_IEEE_F128 +#endif +#define TF_C(x) x##L +#elif __LDBL_MANT_DIG__ == 113 || \ + (__FLT_RADIX__ == 16 && __LDBL_MANT_DIG__ == 28) +// Use long double instead of __float128 if it matches the IEEE 128-bit format +// or the IBM hexadecimal format. +#define CRT_LDBL_128BIT +#define CRT_HAS_F128 +#if __LDBL_MANT_DIG__ == 113 +#define CRT_HAS_IEEE_TF +#define CRT_LDBL_IEEE_F128 +#endif +typedef long double tf_float; +#define TF_C(x) x##L +#elif defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__) +#define CRT_HAS___FLOAT128_KEYWORD +#define CRT_HAS_F128 +// NB: we assume the __float128 type uses IEEE representation. +#define CRT_HAS_IEEE_TF +typedef __float128 tf_float; +#define TF_C(x) x##Q +#endif + +#ifdef CRT_HAS_F128 +typedef union { + uqwords u; + tf_float f; +} tf_bits; +#endif + +// __(u)int128_t is currently needed to compile the *tf builtins as we would +// otherwise need to manually expand the bit manipulation on two 64-bit value. +#if defined(CRT_HAS_128BIT) && defined(CRT_HAS_F128) +#define CRT_HAS_TF_MODE +#endif + +#if __STDC_VERSION__ >= 199901L +typedef float _Complex Fcomplex; +typedef double _Complex Dcomplex; +typedef long double _Complex Lcomplex; +#if defined(CRT_LDBL_128BIT) +typedef Lcomplex Qcomplex; +#define CRT_HAS_NATIVE_COMPLEX_F128 +#elif defined(CRT_HAS___FLOAT128_KEYWORD) +#if defined(__clang_major__) && __clang_major__ > 10 +// Clang prior to 11 did not support __float128 _Complex. +typedef __float128 _Complex Qcomplex; +#define CRT_HAS_NATIVE_COMPLEX_F128 +#elif defined(__GNUC__) && __GNUC__ >= 7 +// GCC does not allow __float128 _Complex, but accepts _Float128 _Complex. +typedef _Float128 _Complex Qcomplex; +#define CRT_HAS_NATIVE_COMPLEX_F128 +#endif +#endif + +#define COMPLEX_REAL(x) __real__(x) +#define COMPLEX_IMAGINARY(x) __imag__(x) +#else +typedef struct { + float real, imaginary; +} Fcomplex; + +typedef struct { + double real, imaginary; +} Dcomplex; + +typedef struct { + long double real, imaginary; +} Lcomplex; + +#define COMPLEX_REAL(x) (x).real +#define COMPLEX_IMAGINARY(x) (x).imaginary +#endif + +#ifdef CRT_HAS_NATIVE_COMPLEX_F128 +#define COMPLEXTF_REAL(x) __real__(x) +#define COMPLEXTF_IMAGINARY(x) __imag__(x) +#elif defined(CRT_HAS_F128) +typedef struct { + tf_float real, imaginary; +} Qcomplex; +#define COMPLEXTF_REAL(x) (x).real +#define COMPLEXTF_IMAGINARY(x) (x).imaginary +#endif + +#endif // CRT_HAS_FLOATING_POINT +#endif // INT_TYPES_H diff --git a/src/bflat/modules/softfloat/builtins/int_util.h b/src/bflat/modules/softfloat/builtins/int_util.h new file mode 100644 index 0000000..c372c2e --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/int_util.h @@ -0,0 +1,47 @@ +//===-- int_util.h - internal utility functions ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is not part of the interface of this library. +// +// This file defines non-inline utilities which are available for use in the +// library. The function definitions themselves are all contained in int_util.c +// which will always be compiled into any compiler-rt library. +// +//===----------------------------------------------------------------------===// + +#ifndef INT_UTIL_H +#define INT_UTIL_H + +/// \brief Trigger a program abort (or panic for kernel code). +#define compilerrt_abort() __compilerrt_abort_impl(__FILE__, __LINE__, __func__) + +NORETURN void __compilerrt_abort_impl(const char *file, int line, + const char *function); + +#define COMPILE_TIME_ASSERT(expr) COMPILE_TIME_ASSERT1(expr, __COUNTER__) +#define COMPILE_TIME_ASSERT1(expr, cnt) COMPILE_TIME_ASSERT2(expr, cnt) +#define COMPILE_TIME_ASSERT2(expr, cnt) \ + typedef char ct_assert_##cnt[(expr) ? 1 : -1] UNUSED + +// Force unrolling the code specified to be repeated N times. +#define REPEAT_0_TIMES(code_to_repeat) /* do nothing */ +#define REPEAT_1_TIMES(code_to_repeat) code_to_repeat +#define REPEAT_2_TIMES(code_to_repeat) \ + REPEAT_1_TIMES(code_to_repeat) \ + code_to_repeat +#define REPEAT_3_TIMES(code_to_repeat) \ + REPEAT_2_TIMES(code_to_repeat) \ + code_to_repeat +#define REPEAT_4_TIMES(code_to_repeat) \ + REPEAT_3_TIMES(code_to_repeat) \ + code_to_repeat + +#define REPEAT_N_TIMES_(N, code_to_repeat) REPEAT_##N##_TIMES(code_to_repeat) +#define REPEAT_N_TIMES(N, code_to_repeat) REPEAT_N_TIMES_(N, code_to_repeat) + +#endif // INT_UTIL_H diff --git a/src/bflat/modules/softfloat/builtins/muldf3.c b/src/bflat/modules/softfloat/builtins/muldf3.c new file mode 100644 index 0000000..f64b522 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/muldf3.c @@ -0,0 +1,25 @@ +//===-- lib/muldf3.c - Double-precision multiplication ------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float multiplication +// with the IEEE-754 default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_mul_impl.inc" + +COMPILER_RT_ABI fp_t __muldf3(fp_t a, fp_t b) { return __mulXf3__(a, b); } + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI fp_t __aeabi_dmul(fp_t a, fp_t b) { return __muldf3(a, b); } +#else +COMPILER_RT_ALIAS(__muldf3, __aeabi_dmul) +#endif +#endif diff --git a/src/bflat/modules/softfloat/builtins/mulsf3.c b/src/bflat/modules/softfloat/builtins/mulsf3.c new file mode 100644 index 0000000..b9cf39a --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/mulsf3.c @@ -0,0 +1,25 @@ +//===-- lib/mulsf3.c - Single-precision multiplication ------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float multiplication +// with the IEEE-754 default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_mul_impl.inc" + +COMPILER_RT_ABI fp_t __mulsf3(fp_t a, fp_t b) { return __mulXf3__(a, b); } + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI fp_t __aeabi_fmul(fp_t a, fp_t b) { return __mulsf3(a, b); } +#else +COMPILER_RT_ALIAS(__mulsf3, __aeabi_fmul) +#endif +#endif diff --git a/src/bflat/modules/softfloat/builtins/negdf2.c b/src/bflat/modules/softfloat/builtins/negdf2.c new file mode 100644 index 0000000..f9ceaa3 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/negdf2.c @@ -0,0 +1,24 @@ +//===-- lib/negdf2.c - double-precision negation ------------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float negation. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +COMPILER_RT_ABI fp_t __negdf2(fp_t a) { return fromRep(toRep(a) ^ signBit); } + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI fp_t __aeabi_dneg(fp_t a) { return __negdf2(a); } +#else +COMPILER_RT_ALIAS(__negdf2, __aeabi_dneg) +#endif +#endif diff --git a/src/bflat/modules/softfloat/builtins/negsf2.c b/src/bflat/modules/softfloat/builtins/negsf2.c new file mode 100644 index 0000000..d59dfe7 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/negsf2.c @@ -0,0 +1,24 @@ +//===-- lib/negsf2.c - single-precision negation ------------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float negation. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +COMPILER_RT_ABI fp_t __negsf2(fp_t a) { return fromRep(toRep(a) ^ signBit); } + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI fp_t __aeabi_fneg(fp_t a) { return __negsf2(a); } +#else +COMPILER_RT_ALIAS(__negsf2, __aeabi_fneg) +#endif +#endif diff --git a/src/bflat/modules/softfloat/builtins/subdf3.c b/src/bflat/modules/softfloat/builtins/subdf3.c new file mode 100644 index 0000000..2100fd3 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/subdf3.c @@ -0,0 +1,27 @@ +//===-- lib/adddf3.c - Double-precision subtraction ---------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float subtraction. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +// Subtraction; flip the sign bit of b and add. +COMPILER_RT_ABI fp_t __subdf3(fp_t a, fp_t b) { + return __adddf3(a, fromRep(toRep(b) ^ signBit)); +} + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI fp_t __aeabi_dsub(fp_t a, fp_t b) { return __subdf3(a, b); } +#else +COMPILER_RT_ALIAS(__subdf3, __aeabi_dsub) +#endif +#endif diff --git a/src/bflat/modules/softfloat/builtins/subsf3.c b/src/bflat/modules/softfloat/builtins/subsf3.c new file mode 100644 index 0000000..ecfc24f --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/subsf3.c @@ -0,0 +1,27 @@ +//===-- lib/subsf3.c - Single-precision subtraction ---------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float subtraction. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +// Subtraction; flip the sign bit of b and add. +COMPILER_RT_ABI fp_t __subsf3(fp_t a, fp_t b) { + return __addsf3(a, fromRep(toRep(b) ^ signBit)); +} + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI fp_t __aeabi_fsub(fp_t a, fp_t b) { return __subsf3(a, b); } +#else +COMPILER_RT_ALIAS(__subsf3, __aeabi_fsub) +#endif +#endif diff --git a/src/bflat/modules/softfloat/builtins/truncdfsf2.c b/src/bflat/modules/softfloat/builtins/truncdfsf2.c new file mode 100644 index 0000000..44a1299 --- /dev/null +++ b/src/bflat/modules/softfloat/builtins/truncdfsf2.c @@ -0,0 +1,21 @@ +//===-- lib/truncdfsf2.c - double -> single conversion ------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#define SRC_DOUBLE +#define DST_SINGLE +#include "fp_trunc_impl.inc" + +COMPILER_RT_ABI float __truncdfsf2(double a) { return __truncXfYf2__(a); } + +#if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI float __aeabi_d2f(double a) { return __truncdfsf2(a); } +#else +COMPILER_RT_ALIAS(__truncdfsf2, __aeabi_d2f) +#endif +#endif diff --git a/src/bflat/modules/softfloat/fmod.c b/src/bflat/modules/softfloat/fmod.c new file mode 100644 index 0000000..6849722 --- /dev/null +++ b/src/bflat/modules/softfloat/fmod.c @@ -0,0 +1,68 @@ +#include +#include + +double fmod(double x, double y) +{ + union {double f; uint64_t i;} ux = {x}, uy = {y}; + int ex = ux.i>>52 & 0x7ff; + int ey = uy.i>>52 & 0x7ff; + int sx = ux.i>>63; + uint64_t i; + + /* in the followings uxi should be ux.i, but then gcc wrongly adds */ + /* float load/store to inner loops ruining performance and code size */ + uint64_t uxi = ux.i; + + if (uy.i<<1 == 0 || isnan(y) || ex == 0x7ff) + return (x*y)/(x*y); + if (uxi<<1 <= uy.i<<1) { + if (uxi<<1 == uy.i<<1) + return 0*x; + return x; + } + + /* normalize x and y */ + if (!ex) { + for (i = uxi<<12; i>>63 == 0; ex--, i <<= 1); + uxi <<= -ex + 1; + } else { + uxi &= -1ULL >> 12; + uxi |= 1ULL << 52; + } + if (!ey) { + for (i = uy.i<<12; i>>63 == 0; ey--, i <<= 1); + uy.i <<= -ey + 1; + } else { + uy.i &= -1ULL >> 12; + uy.i |= 1ULL << 52; + } + + /* x mod y */ + for (; ex > ey; ex--) { + i = uxi - uy.i; + if (i >> 63 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if (i >> 63 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + for (; uxi>>52 == 0; uxi <<= 1, ex--); + + /* scale result */ + if (ex > 0) { + uxi -= 1ULL << 52; + uxi |= (uint64_t)ex << 52; + } else { + uxi >>= -ex + 1; + } + uxi |= (uint64_t)sx << 63; + ux.i = uxi; + return ux.f; +} diff --git a/src/bflat/modules/softfloat/fmodf.c b/src/bflat/modules/softfloat/fmodf.c new file mode 100644 index 0000000..ff58f93 --- /dev/null +++ b/src/bflat/modules/softfloat/fmodf.c @@ -0,0 +1,65 @@ +#include +#include + +float fmodf(float x, float y) +{ + union {float f; uint32_t i;} ux = {x}, uy = {y}; + int ex = ux.i>>23 & 0xff; + int ey = uy.i>>23 & 0xff; + uint32_t sx = ux.i & 0x80000000; + uint32_t i; + uint32_t uxi = ux.i; + + if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) + return (x*y)/(x*y); + if (uxi<<1 <= uy.i<<1) { + if (uxi<<1 == uy.i<<1) + return 0*x; + return x; + } + + /* normalize x and y */ + if (!ex) { + for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); + uxi <<= -ex + 1; + } else { + uxi &= -1U >> 9; + uxi |= 1U << 23; + } + if (!ey) { + for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); + uy.i <<= -ey + 1; + } else { + uy.i &= -1U >> 9; + uy.i |= 1U << 23; + } + + /* x mod y */ + for (; ex > ey; ex--) { + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + for (; uxi>>23 == 0; uxi <<= 1, ex--); + + /* scale result up */ + if (ex > 0) { + uxi -= 1U << 23; + uxi |= (uint32_t)ex << 23; + } else { + uxi >>= -ex + 1; + } + uxi |= sx; + ux.i = uxi; + return ux.f; +} diff --git a/src/bflat/modules/softfloat/module.c b/src/bflat/modules/softfloat/module.c new file mode 100644 index 0000000..7d12895 --- /dev/null +++ b/src/bflat/modules/softfloat/module.c @@ -0,0 +1,144 @@ +/** + * @file + * @brief Soft-float support module for no-F/D RISC-V targets + * + * Copyright (C) 2026 Demerzel Solutions Limited (Nethermind) + * + * @author Maksim Menshikov + */ + +/* + * Counterpart of the nofp module for the soft-float configuration: instead of + * trapping, floating point WORKS, computed in integer code. + * + * The JIT (dotnet-riscv fixup 28) lowers all FP IR into calls that follow the + * compiler-rt/libgcc soft-float ABI; the builtins themselves are vendored + * under builtins/ (compiler-rt, Apache-2.0 WITH LLVM-exception) and compiled + * for rv64im by module_srcs.lst. + * + * This file covers what the builtins do not: + * + * - RhpDbl2Lng/RhpDbl2ULng and the int64->FP family: the runtime blobs ship + * these compiled for rv64gc (hard-float). They are diverted here with + * --wrap (see module_params.yml) so the hard-float archive members never + * enter the link; the replacements carry the .NET saturating semantics + * (NaN -> 0, out-of-range -> Min/Max) and compile to soft-float calls. + * + * - fmod/fmodf (CORINFO_HELP_DBLREM/FLTREM): musl's members are hard-float, + * so the ilc-referenced symbols are wrapped to the vendored musl sources + * (MIT), which are pure bit manipulation and carry no FP instruction on + * any -march. + */ + +#include + +/* Vendored musl fmod/fmodf as the __wrap_ definitions. math.h declarations + * are renamed along with the definitions by the defines below. The ACSL + * contracts sit on forward declarations; Frama-C attaches a declaration + * spec to the (renamed) definitions pulled in by the includes. */ + +/*@ // IEEE-754 remainder as computed by musl's bit-manipulation fmod: + // finite arguments with y != 0 produce the exact remainder; NaN + // operands, infinite x or zero y produce NaN. Pure integer code. + assigns \nothing; +*/ +double __wrap_fmod(double x, double y); + +/*@ assigns \nothing; */ +float __wrap_fmodf(float x, float y); + +#define fmod __wrap_fmod +#include "fmod.c" +#undef fmod + +#define fmodf __wrap_fmodf +#include "fmodf.c" +#undef fmodf + +/* .NET-semantics FP->int conversions (see nativeaot MathHelpers.cpp). + * The comparisons compile into calls to the vendored soft-float builtins. */ + +/*@ // .NET double -> uint64 conversion semantics (MathHelpers.cpp): + // NaN and negative values saturate to 0, values at or above 2^64 + // saturate to UINT64_MAX, the rest truncate toward zero. + assigns \nothing; + behavior nan_or_nonpositive: + assumes \is_NaN(val) || val <= 0.0; + ensures \result == 0; + behavior too_big: + assumes \is_finite(val) && val >= 18446744073709551616.0; + ensures \result == UINT64_MAX; +*/ +uint64_t +__wrap_RhpDbl2ULng(double val) +{ + const double uint64_max_plus_1 = 4294967296.0 * 4294967296.0; + + return (val > 0) ? ((val >= uint64_max_plus_1) ? UINT64_MAX + : (uint64_t)val) + : 0; +} + +/*@ // .NET double -> int64 conversion semantics: NaN maps to 0, values + // beyond either end of the int64 range saturate to Min/MaxValue, the + // rest truncate toward zero. + assigns \nothing; + behavior nan: + assumes \is_NaN(val); + ensures \result == 0; + behavior too_small: + assumes \is_finite(val) && val <= -9223372036854775808.0; + ensures \result == INT64_MIN; + behavior too_big: + assumes \is_finite(val) && val >= 9223372036854775808.0; + ensures \result == INT64_MAX; +*/ +int64_t +__wrap_RhpDbl2Lng(double val) +{ + const double int64_min = -2147483648.0 * 4294967296.0; + const double int64_max = 2147483648.0 * 4294967296.0; + + return (val != val) ? 0 + : (val <= int64_min) ? INT64_MIN + : (val >= int64_max) ? INT64_MAX + : (int64_t)val; +} + +/* int64 -> FP: plain casts, exact via the builtins. */ + +/*@ // int64 -> double, round to nearest even (soft-float builtin underneath). + assigns \nothing; +*/ +double +__wrap_RhpLng2Dbl(int64_t val) +{ + return (double)val; +} + +/*@ // uint64 -> double, round to nearest even (soft-float builtin underneath). + assigns \nothing; +*/ +double +__wrap_RhpULng2Dbl(uint64_t val) +{ + return (double)val; +} + +/*@ // int64 -> float, round to nearest even (soft-float builtin underneath). + assigns \nothing; +*/ +float +__wrap_RhpLng2Flt(int64_t val) +{ + return (float)val; +} + +/*@ // uint64 -> float, round to nearest even (soft-float builtin underneath). + assigns \nothing; +*/ +float +__wrap_RhpULng2Flt(uint64_t val) +{ + return (float)val; +} diff --git a/src/bflat/modules/softfloat/module_params.yml b/src/bflat/modules/softfloat/module_params.yml new file mode 100644 index 0000000..e49d758 --- /dev/null +++ b/src/bflat/modules/softfloat/module_params.yml @@ -0,0 +1,69 @@ +# Linker options owned by the softfloat module (packed as softfloat.params.yml). +# Applied for zisk/zisk_sim targets built without the F extension, where the +# JIT lowers FP into soft-float calls (dotnet-riscv fixups 26-28) instead of +# the nofp trap model. +options: + # Multi-file module: build.sh compiles every listed source as a plain ELF + # object and merges them with ld -r (no LTO, so the common rv64imad flags + # never re-codegen the builtins). + sources: + - value: module.c + - value: builtins/adddf3.c + - value: builtins/addsf3.c + - value: builtins/comparedf2.c + - value: builtins/comparesf2.c + - value: builtins/divdf3.c + - value: builtins/divsf3.c + - value: builtins/extendsfdf2.c + - value: builtins/fixdfdi.c + - value: builtins/fixdfsi.c + - value: builtins/fixsfdi.c + - value: builtins/fixsfsi.c + - value: builtins/fixunsdfdi.c + - value: builtins/fixunsdfsi.c + - value: builtins/fixunssfdi.c + - value: builtins/fixunssfsi.c + - value: builtins/floatdidf.c + - value: builtins/floatdisf.c + - value: builtins/floatsidf.c + - value: builtins/floatsisf.c + - value: builtins/floatundidf.c + - value: builtins/floatundisf.c + - value: builtins/floatunsidf.c + - value: builtins/floatunsisf.c + - value: builtins/fp_mode.c + - value: builtins/muldf3.c + - value: builtins/mulsf3.c + - value: builtins/negdf2.c + - value: builtins/negsf2.c + - value: builtins/subdf3.c + - value: builtins/subsf3.c + - value: builtins/truncdfsf2.c + + # Compiler flags for the module sources: the vendored compiler-rt builtins + # must be compiled strictly for rv64im so no F/D/C/A/Zicond encoding can + # appear. + cc: + - value: -march=rv64im + - value: -mabi=lp64 + - value: -mcmodel=medany + - value: -mno-relax + - value: -O2 + - value: -fno-builtin + - value: -ffreestanding + - value: -fno-stack-protector + - value: -Ibuiltins + ld: + # The runtime blobs ship these compiled for rv64gc (hard-float); divert + # every reference to the soft __wrap_ implementations in module.c so the + # hard-float archive members never enter the link. + - value: --wrap=RhpDbl2Lng + - value: --wrap=RhpDbl2ULng + - value: --wrap=RhpLng2Dbl + - value: --wrap=RhpULng2Dbl + - value: --wrap=RhpLng2Flt + - value: --wrap=RhpULng2Flt + # CORINFO_HELP_DBLREM/FLTREM resolve to fmod/fmodf; musl's members are + # hard-float, the wrapped ones (vendored musl sources) are integer-only. + - value: --wrap=fmod + - value: --wrap=fmodf diff --git a/src/bflat/modules/zisk_subst/module_params.yml b/src/bflat/modules/zisk_subst/module_params.yml index 63dacdf..8b1d92d 100644 --- a/src/bflat/modules/zisk_subst/module_params.yml +++ b/src/bflat/modules/zisk_subst/module_params.yml @@ -11,7 +11,14 @@ # BuildCommand.TryCompileZiskSnippets) # # Entries take the same libc/arch/os conditions as options.ld. +# +# The FP-elimination surface (zisk.nofp.substitutions.xml and every snippet) +# is only meaningful when the guest has no floating point at all; a soft-float +# link keeps the original CoreLib bodies (fpu condition, see ReadModuleParams). options: ilc: - substitutions: zisk.substitutions.xml + - substitutions: zisk.nofp.substitutions.xml + fpu: none - snippets: zisk.snippets.cs + fpu: none diff --git a/src/bflat/modules/zisk_subst/zisk.nofp.substitutions.xml b/src/bflat/modules/zisk_subst/zisk.nofp.substitutions.xml new file mode 100644 index 0000000..4630f9d --- /dev/null +++ b/src/bflat/modules/zisk_subst/zisk.nofp.substitutions.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bflat/modules/zisk_subst/zisk.substitutions.xml b/src/bflat/modules/zisk_subst/zisk.substitutions.xml index c26cba7..de22497 100644 --- a/src/bflat/modules/zisk_subst/zisk.substitutions.xml +++ b/src/bflat/modules/zisk_subst/zisk.substitutions.xml @@ -88,115 +88,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -