All files / TRANSPILE/3-Render/codegen/generators/support HelperGenerator.ts

98% Statements 49/50
95.83% Branches 23/24
100% Functions 6/6
100% Lines 47/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193                                          47x         16x                               47x       1250x 1093x     157x   157x       6x 6x                         151x                         157x   157x 166x 166x     166x 164x       157x             47x     1242x 1229x     13x           13x   13x   13x 104x 104x   104x 89x     15x     15x 12x       15x 4x       13x                 47x       1232x 2x 9x 2x             2x 3x 3x 3x             3x 3x             2x       47x              
/**
 * Helper function generators for overflow-safe arithmetic and safe division.
 * Extracted from CodeGenerator.ts.
 *
 * Portability note: Uses __builtin_add_overflow, __builtin_sub_overflow, and
 * __builtin_mul_overflow intrinsics (GCC 5+, Clang 3.4+). C-Next targets embedded
 * systems using arm-none-eabi-gcc, so these are available. MSVC is not supported.
 *
 * Issue #707: Refactored to use OverflowHelperTemplates for reduced duplication.
 */
import TYPE_MAP from "../../types/TYPE_MAP";
import OverflowHelperTemplates from "./OverflowHelperTemplates";
import SaturatingCast from "../../helpers/SaturatingCast";
import CppModeHelper from "../../helpers/CppModeHelper";
import ReservedCnxName from "../../../../../utils/ReservedCnxName";
import ToolchainRequirements from "../../../../../instrumentation/ToolchainRequirements";
 
/**
 * Generate a safe arithmetic helper function (div or mod).
 * Extracted to eliminate duplication between safe_div and safe_mod generation.
 */
const generateSafeArithmeticHelper = (
  opName: string,
  opSymbol: string,
  cnxType: string,
  cType: string,
): string[] => [
  `static inline bool cnx_safe_${opName}_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
  `    if (divisor == 0) {`,
  `        *output = defaultValue;`,
  `        return true;  // Error occurred`,
  `    }`,
  `    *output = numerator ${opSymbol} divisor;`,
  `    return false;  // Success`,
  `}`,
  "",
];
 
/**
 * Generate all needed overflow helper functions
 * ADR-044: Overflow helper functions with clamping or panic behavior
 */
const generateOverflowHelpers = (
  usedClampOps: ReadonlySet<string>,
  debugMode: boolean,
): string[] => {
  if (usedClampOps.size === 0) {
    return [];
  }
 
  const lines: string[] = [];
 
  if (debugMode) {
    // Issue #1143: this branch, and only this branch, pulls in a hosted libc.
    // Release-mode clamp helpers are freestanding-safe, so the requirement is
    // recorded here rather than wherever a clamp op happens to be registered.
    ToolchainRequirements.record("overflow-panic-hosted-libc");
    lines.push(
      "// ADR-044: Debug overflow helper functions (panic on overflow)",
      "#include <limits.h>",
      "#include <stdio.h>",
      "#include <stdlib.h>",
      "",
      "/* ADR-044 / Issue #94: the second parameter is the WIDER type, not the value type.",
      "   Narrowing it first would let an out-of-range operand truncate INTO range and defeat",
      "   the check: cnx_clamp_add_u8(0, 256) must saturate to 255, but (uint8_t)256 is 0, so a",
      "   uint8_t parameter would return 0 -- the opposite of saturation. */",
      "",
    );
  } else {
    lines.push(
      "// ADR-044: Overflow helper functions",
      "#include <limits.h>",
      "",
      "/* ADR-044 / Issue #94: the second parameter is the WIDER type, not the value type.",
      "   Narrowing it first would let an out-of-range operand truncate INTO range and defeat",
      "   the check: cnx_clamp_add_u8(0, 256) must saturate to 255, but (uint8_t)256 is 0, so a",
      "   uint8_t parameter would return 0 -- the opposite of saturation. */",
      "",
    );
  }
 
  // Sort for deterministic output
  const sortedOps = Array.from(usedClampOps).sort((a, b) => a.localeCompare(b));
 
  for (const op of sortedOps) {
    const [operation, cnxType] = op.split("_");
    const helper = debugMode
      ? OverflowHelperTemplates.generatePanicHelper(operation, cnxType)
      : OverflowHelperTemplates.generateClampHelper(operation, cnxType);
    if (helper) {
      lines.push(helper, "");
    }
  }
 
  return lines;
};
 
/**
 * Generate safe division helper functions for used integer types only
 * ADR-051: Safe division helpers that return error flag on division by zero
 */
const generateSafeDivHelpers = (
  usedSafeDivOps: ReadonlySet<string>,
): string[] => {
  if (usedSafeDivOps.size === 0) {
    return [];
  }
 
  const lines: string[] = [];
 
  // Issue #1108: the <stdbool.h> dependency (helpers return a bool error flag)
  // is signalled via requireInclude("stdbool") when the safe-div effect is
  // applied, so it flows through the single addAutoIncludes path. Emitting it
  // here too would duplicate the include.
  lines.push("// ADR-051: Safe division helper functions", "");
 
  const integerTypes = ["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64"];
 
  for (const cnxType of integerTypes) {
    const needsDiv = usedSafeDivOps.has(`div_${cnxType}`);
    const needsMod = usedSafeDivOps.has(`mod_${cnxType}`);
 
    if (!needsDiv && !needsMod) {
      continue; // Skip types that aren't used
    }
 
    const cType = TYPE_MAP[cnxType];
 
    // Generate safe_div helper if needed
    if (needsDiv) {
      lines.push(...generateSafeArithmeticHelper("div", "/", cnxType, cType));
    }
 
    // Generate safe_mod helper if needed
    if (needsMod) {
      lines.push(...generateSafeArithmeticHelper("mod", "%", cnxType, cType));
    }
  }
 
  return lines;
};
 
/**
 * #1668: the single-evaluation saturating casts a file calls, one per
 * source/target pair (`"f32_u8"`). Each body is ADR-024's bounded ternary,
 * from the one shape `SaturatingCast` writes, over its parameter -- which is
 * the operand, evaluated once, at the call.
 */
const generateCastHelpers = (
  keys: readonly string[],
  cppMode: boolean,
): string[] => {
  if (keys.length === 0) return [];
  const cast = (type: string, expr: string): string =>
    CppModeHelper.cast(type, expr, { cppMode });
  const lines: string[] = [
    "/* ADR-024 / Issue #1668: a saturating float-to-integer cast whose operand has a side",
    "   effect -- a call, or a volatile or atomic read -- calls one of these, so the operand is",
    "   evaluated once. The inline bounded ternary reads its operand up to three times,",
    "   repeating the side effect. */",
    "",
  ];
  for (const key of keys) {
    const [sourceType, targetType] = key.split("_");
    const cType = TYPE_MAP[targetType];
    const body = SaturatingCast.expression(
      "value",
      sourceType,
      targetType,
      cType,
      cast,
    );
    Iif (cType === undefined || body === null) continue;
    lines.push(
      `static inline ${cType} ${ReservedCnxName.castHelper(sourceType, targetType)}(${SaturatingCast.floatCType(sourceType)} value) {`,
      `    return ${body};`,
      "}",
      "",
    );
  }
  return lines;
};
 
// Export as an object for consistent module pattern
const helperGenerators = {
  generateOverflowHelpers,
  generateSafeDivHelpers,
  generateCastHelpers,
};
 
export default helperGenerators;