All files / transpiler/output/codegen/generators/statements AtomicGenerator.ts

100% Statements 37/37
100% Branches 17/17
100% Functions 5/5
100% Lines 37/37

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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249                                      13x                             13x                         13x                       13x                               13x                     27x         22x   5x                           18x 18x     18x 18x 9x         9x             9x                             8x 8x 8x 8x     8x       8x           8x                             9x     9x           9x 9x 6x         6x   3x       9x             9x                                         6x     6x     6x 3x             3x         13x                
/**
 * Atomic Operation Generators (ADR-053 A3)
 *
 * Generates C code for atomic Read-Modify-Write operations (ADR-049):
 * - LDREX/STREX loops for platforms with exclusive access support
 * - PRIMASK-based wrappers for interrupt-safe operations
 *
 * These are helper functions called from assignment generation,
 * not top-level statement generators.
 */
import TTypeInfo from "../../types/TTypeInfo";
import IGeneratorOutput from "../IGeneratorOutput";
import TGeneratorEffect from "../TGeneratorEffect";
import ITargetCapabilities from "../../types/ITargetCapabilities";
import TYPE_WIDTH from "../../types/TYPE_WIDTH";
 
/**
 * Maps C-Next types to C types (for atomic operations)
 */
const TYPE_MAP: Record<string, string> = {
  u8: "uint8_t",
  u16: "uint16_t",
  u32: "uint32_t",
  u64: "uint64_t",
  i8: "int8_t",
  i16: "int16_t",
  i32: "int32_t",
  i64: "int64_t",
};
 
/**
 * ADR-049: LDREX intrinsic map for atomic operations
 * Maps C-Next types to ARM LDREX (Load-Exclusive) instructions
 */
const LDREX_MAP: Record<string, string> = {
  u8: "__LDREXB",
  i8: "__LDREXB",
  u16: "__LDREXH",
  i16: "__LDREXH",
  u32: "__LDREXW",
  i32: "__LDREXW",
};
 
/**
 * ADR-049: STREX intrinsic map for atomic operations
 * Maps C-Next types to ARM STREX (Store-Exclusive) instructions
 */
const STREX_MAP: Record<string, string> = {
  u8: "__STREXB",
  i8: "__STREXB",
  u16: "__STREXH",
  i16: "__STREXH",
  u32: "__STREXW",
  i32: "__STREXW",
};
 
/**
 * Map compound operators to simple operators
 */
const SIMPLE_OP_MAP: Record<string, string> = {
  "+=": "+",
  "-=": "-",
  "*=": "*",
  "/=": "/",
  "%=": "%",
  "&=": "&",
  "|=": "|",
  "^=": "^",
  "<<=": "<<",
  ">>=": ">>",
};
 
/**
 * Map compound operators to clamp helper operation names
 */
const CLAMP_OP_MAP: Record<string, string> = {
  "+=": "add",
  "-=": "sub",
  "*=": "mul",
};
 
/**
 * Check if clamp behavior applies and return helper operation name.
 * Returns null if clamp doesn't apply (wrap behavior, float, or unsupported op).
 */
function getClampHelperOp(cOp: string, typeInfo: TTypeInfo): string | null {
  if (
    typeInfo.overflowBehavior === "clamp" &&
    TYPE_WIDTH[typeInfo.baseType] &&
    !typeInfo.baseType.startsWith("f") // Floats use native C arithmetic
  ) {
    return CLAMP_OP_MAP[cOp] || null;
  }
  return null;
}
 
/**
 * Generate the inner operation for atomic RMW.
 * Handles clamp/wrap behavior for arithmetic operations.
 *
 * @returns Object with code and effects (may include helper effects for clamp)
 */
function generateInnerAtomicOp(
  cOp: string,
  value: string,
  typeInfo: TTypeInfo,
): IGeneratorOutput {
  const effects: TGeneratorEffect[] = [];
  const simpleOp = SIMPLE_OP_MAP[cOp] || "+";
 
  // Handle clamp behavior for arithmetic operations (integers only)
  const helperOp = getClampHelperOp(cOp, typeInfo);
  if (helperOp) {
    effects.push({
      type: "helper",
      operation: helperOp,
      cnxType: typeInfo.baseType,
    });
    return {
      code: `cnx_clamp_${helperOp}_${typeInfo.baseType}(__old, ${value})`,
      effects,
    };
  }
 
  // For wrap behavior, floats, or non-clamp ops, use natural arithmetic
  return { code: `__old ${simpleOp} ${value}`, effects };
}
 
/**
 * Generate LDREX/STREX retry loop for atomic RMW.
 * Uses ARM exclusive access instructions for lock-free atomics.
 *
 * @returns Object with code and effects (includes cmsis header)
 */
function generateLdrexStrexLoop(
  target: string,
  innerOp: string,
  typeInfo: TTypeInfo,
  innerEffects: readonly TGeneratorEffect[],
): IGeneratorOutput {
  const effects: TGeneratorEffect[] = [...innerEffects];
  const ldrex = LDREX_MAP[typeInfo.baseType];
  const strex = STREX_MAP[typeInfo.baseType];
  const cType = TYPE_MAP[typeInfo.baseType];
 
  // Mark that we need CMSIS headers
  effects.push({ type: "include", header: "cmsis" });
 
  // Generate LDREX/STREX retry loop
  // Uses do-while because we always need at least one attempt
  const code = `do {
    ${cType} __old = ${ldrex}(&${target});
    ${cType} __new = ${innerOp};
    if (${strex}(__new, &${target}) == 0) break;
} while (1);`;
 
  return { code, effects };
}
 
/**
 * Generate PRIMASK-based atomic wrapper.
 * Disables all interrupts during the RMW operation.
 *
 * @returns Object with code and effects (includes cmsis header, may include helper)
 */
function generatePrimaskWrapper(
  target: string,
  cOp: string,
  value: string,
  typeInfo: TTypeInfo,
): IGeneratorOutput {
  const effects: TGeneratorEffect[] = [];
 
  // Mark that we need CMSIS headers
  effects.push({ type: "include", header: "cmsis" });
 
  // Generate the actual assignment operation inside the critical section
  let assignment: string;
 
  // Handle clamp behavior (integers only)
  const helperOp = getClampHelperOp(cOp, typeInfo);
  if (helperOp) {
    effects.push({
      type: "helper",
      operation: helperOp,
      cnxType: typeInfo.baseType,
    });
    assignment = `${target} = cnx_clamp_${helperOp}_${typeInfo.baseType}(${target}, ${value});`;
  } else {
    assignment = `${target} ${cOp} ${value};`;
  }
 
  // Generate PRIMASK save/restore wrapper
  const code = `{
    uint32_t __primask = __get_PRIMASK();
    __disable_irq();
    ${assignment}
    __set_PRIMASK(__primask);
}`;
 
  return { code, effects };
}
 
/**
 * ADR-049: Generate atomic Read-Modify-Write operation.
 * Uses LDREX/STREX on platforms that support it, otherwise PRIMASK.
 *
 * @param target - The target variable expression
 * @param cOp - The C compound assignment operator (+=, -=, etc.)
 * @param value - The value expression
 * @param typeInfo - Type information for the target
 * @param targetCapabilities - Platform capabilities
 * @returns Generated code and effects
 */
function generateAtomicRMW(
  target: string,
  cOp: string,
  value: string,
  typeInfo: TTypeInfo,
  targetCapabilities: ITargetCapabilities,
): IGeneratorOutput {
  const baseType = typeInfo.baseType;
 
  // Generate the inner operation (handles clamp/wrap)
  const innerResult = generateInnerAtomicOp(cOp, value, typeInfo);
 
  // Use LDREX/STREX if available for this type, otherwise PRIMASK fallback
  if (targetCapabilities.hasLdrexStrex && LDREX_MAP[baseType]) {
    return generateLdrexStrexLoop(
      target,
      innerResult.code,
      typeInfo,
      innerResult.effects,
    );
  } else {
    return generatePrimaskWrapper(target, cOp, value, typeInfo);
  }
}
 
// Export all atomic generators
const atomicGenerators = {
  generateAtomicRMW,
  generateInnerAtomicOp,
  generateLdrexStrexLoop,
  generatePrimaskWrapper,
};
 
export default atomicGenerators;