All files / transpiler/output/codegen/generators/support HelperGenerator.ts

100% Statements 36/36
100% Branches 18/18
100% Functions 4/4
100% Lines 35/35

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                                    29x         16x                               29x       1073x 973x     100x   100x       6x 6x                         94x                         100x   100x 105x 105x     105x 103x       100x             29x     1065x 1052x     13x           13x   13x   13x 104x 104x   104x 89x     15x     15x 12x       15x 4x       13x       29x            
/**
 * 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 CodeGenState from "../../../../state/CodeGenState";
 
/**
 * 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.
    CodeGenState.requireToolchain("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;
};
 
// Export as an object for consistent module pattern
const helperGenerators = {
  generateOverflowHelpers,
  generateSafeDivHelpers,
};
 
export default helperGenerators;