Skip to content
cccskills
FreePublished on GitHubEnglish overview

Builtin functions

Use this skill to implement builtin functions in the Carbon compiler, from registering a function name and checking its signature to evaluating constant calls and generating LLVM IR for runtime calls. It also covers prelude bindings and focused tests.

OriginalShow the original description

Instructions for registering, mapping, constant evaluating, and lowering builtin functions in the Carbon toolchain.

carbon-language/carbon-lang33,929 stars(GitHub 33,929 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Registering a new Carbon builtin function
  • Adding compile-time evaluation behavior
  • Lowering runtime builtins to LLVM IR
  • Testing builtin checking and code generation

Overview

What it does

This skill explains the workflow for adding builtin functions to the Carbon compiler. It covers registering a function kind, mapping its name to parameter constraints, specifying whether it runs only at compile time, implementing constant evaluation, lowering runtime calls to LLVM IR, and adding the binding in the Prelude. It also describes range checks and diagnostics for integer and floating-point values, plus tests that use a minimal Prelude to validate a builtin on its own.

When it helps

Use this skill when creating or changing Carbon builtins. It can help you decide where to implement compile-time evaluation and runtime code generation, and how to connect the function to the Prelude and test it in isolation.

Example requests

  • “Show me how to register a builtin and implement its constant evaluation.”
  • “Where should this builtin be lowered to LLVM IR?”
  • “Create a test that checks a builtin without depending on the Prelude.”

Notes

This skill assumes familiarity with C++, LLVM IR, and the Carbon compiler structure.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
AGoodIt covers compiler implementation and tests, with no external sending or deletion operations described.
Easy setupDoes it need few tools or API keys, and are they documented?
BPartly lackingIt assumes C++, LLVM IR, and Carbon compiler knowledge but provides no environment setup steps.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (1)

  • SKILL.md11.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Builtin Functions in the Carbon Toolchain

<!-- Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -->

Builtin functions are compiler-recognized primitives mapping directly from Carbon code expressions (via standard prelude bindings) to optimized backend execution. This document defines the complete structural workflow, C++ patterns, constant evaluation logic, machine lowering mechanics, library bindings, and validation strategies required to implement builtin functions in the Carbon compiler.


Technical Flow & Lifecycle

graph TD
  Src[Carbon Source Code] -->|Prelude Map| Sem[Semantic Analysis / SemIR]
  Sem -->|Signature Constraint| Sig[builtin_function_kind.cpp]
  Sem -->|Phase Evaluation| Eval[eval.cpp Constant Interpreter]
  Sem -->|Machine Codegen| Lower[handle_call.cpp LLVM Lowering]
  Eval -->|Diagnostics| Diag[diagnostics/kind.def]
  Lower -->|Native Instructions| LLVM[LLVM IR Generation]

Adding a builtin function involves a 5-step integration:

  1. Define the Builtin Kind: Register the enum in builtin_function_kind.def.
  2. Signature & Compile-Time Registry: Declare the mapping name, parameter constraints, and compile-time evaluation residency in builtin_function_kind.cpp.
  3. Compile-Time Interpreter Support: Wire constant evaluation hooks and bounds/exception diagnostics in eval.cpp.
  4. LLVM IR Lowering Support: Connect target machine generation in handle_call.cpp.
  5. Prelude Library Mapping: Bind primitive interfaces to named builtins under core/prelude/.

Detailed Step-by-Step Implementation Guide

Step 1: Kind Definition & Registration

Register your builtin function name using the X-macro in builtin_function_kind.def:

// toolchain/sem_ir/builtin_function_kind.def

// Converts an integer type to a floating-point type.
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntConvertFloat)

Step 2: Signature Validation & Compile-Time Residence

Inside builtin_function_kind.cpp:

  1. Define Parameter Constraints: If the parameter requires novel constraints (e.g. "must be a float type"), define a template constraint struct checking the matching SemIR type instruction (such as FloatType or FloatLiteralType). Use pre-established semantic helpers:

    • TypeParam<I, T>: Ensures different parameters resolve to identical type structures (e.g., generic constraint matching).
    • AnyInt, AnyFloat, AnySizedInt, AnySizedFloat, CharCompatible, StdInitializerList, NoReturn.
  2. Map Literal Name & Register Constraint Signature: Declare a BuiltinInfo constant inside namespace BuiltinFunctionInfo matching the macro-defined name:

    // toolchain/sem_ir/builtin_function_kind.cpp
    
    constexpr BuiltinInfo IntConvertFloat = {
        "int.convert_float", ValidateSignature<auto(AnyInt)->AnyFloat>};
    
  3. Establish Compile-Time Residency Status: Update BuiltinFunctionKind::IsCompTimeOnly to determine if a call requires compile-time evaluation:

    • Checked/Diagnostics Primitives: Return true immediately. Runtime lowering of these is illegal (e.g. IntConvertFloatChecked).
    • Runtime Primitives: Return AnyLiteralTypes(sem_ir, arg_ids, return_type_id) to enforce that expressions involving unsized literal values (like IntLiteral or FloatLiteral) are evaluated exclusively at compile-time (as they lack runtime representation).

Step 3: Constant Evaluation Support

Wire the interpreter inside eval.cpp to execute compile-time computations:

  1. Implement Constant Evaluation Logic:

    • Handle the builtin case inside MakeConstantForBuiltinCall (which processes the compile-time execution of the call).

    • Confirm type validation phase is Phase::Concrete to reject incomplete bindings:

      case SemIR::BuiltinFunctionKind::IntConvertFloat: {
        if (phase != Phase::Concrete) {
          return MakeConstantResult(context, call, phase);
        }
        return PerformIntToFloatConvert(context, loc_id, arg_ids[0], call.type_id,
                                        /*require_exact=*/false);
      }
      
    • Extract inputs safely from local value stores (e.g. context.ints().Get(arg.int_id) or context.floats().Get(arg.float_id)).

    • Leverage high-precision LLVM mathematical structures (llvm::APInt, llvm::APFloat, llvm::APSInt) to handle custom bits and signedness safely.

  2. Diagnose Invalid Parameters or Exceptions:

    • Define compile-time diagnostics inside kind.def:

      // toolchain/diagnostics/kind.def
      CARBON_DIAGNOSTIC_KIND(IntTooLargeForFloatType)
      
    • Emplace localized diagnostic formatting messages where they are caught in eval.cpp:

      CARBON_DIAGNOSTIC(IntTooLargeForFloatType, Error,
                        "integer value {0} too large for floating-point type {1}",
                        llvm::APSInt, SemIR::TypeId);
      context.emitter().Emit(loc_id, IntTooLargeForFloatType, val, dest_type_id);
      
    • Return SemIR::ErrorInst::ConstantId to gracefully abort invalid constant generation rather than crashing the compiler.

  3. Fast-Path Range Limits:

    • Before evaluating expensive math operations on giant exponents (e.g. 1.0e1000000), executing range limits check against dest_width + 64 (sized) or IntStore::MaxIntWidth (unsized) is mandatory to prevent out-of-bounds calculations and compile-time memory exhaustion.

Step 4: Machine Code Generation (LLVM Lowering)

Inside handle_call.cpp:

  1. Map to Native LLVM Instructions: For runtime-eligible builtins, map the call inside HandleBuiltinCall to native LLVM IR builder methods:

    case SemIR::BuiltinFunctionKind::IntConvertFloat: {
      auto* operand = context.GetValue(arg_ids[0]);
      auto* dest_type = context.GetTypeOfInst(inst_id);
      bool is_signed = IsSignedInt(context, arg_ids[0]);
      context.SetLocal(
          inst_id, is_signed
                       ? context.builder().CreateSIToFP(operand, dest_type)
                       : context.builder().CreateUIToFP(operand, dest_type));
      return;
    }
    
  2. Assert on Compile-Time-Only Builtins: Throw a hard assertion on lowering-cases for checked validator builtins that should never hit code generation:

    case SemIR::BuiltinFunctionKind::IntConvertFloatChecked: {
      CARBON_CHECK(builtin_kind.IsCompTimeOnly(
          context.sem_ir(), arg_ids,
          context.sem_ir().insts().Get(inst_id).type_id()));
      CARBON_FATAL("Missing constant value for call to comptime-only function");
    }
    

Step 5: Standard Library Prelude Integration

Map the standard library primitive interfaces to your newly minted named builtins under core/prelude/:

  • Primitive Mappings: Bind Carbon methods directly to string-literal builtin equivalents:

    fn Convert[self: Self]() -> Float(To) = "int.convert_float";
    
  • Strict Orphan Rule Compliance: Carbon's orphan rules prohibit implementing interfaces where neither the type nor the interface is locally defined in the backing source module.

    • Literal Conversions: Literal types (like FloatLiteral, IntLiteral) do not have backing Carbon source files. Therefore, an impl of UnsafeAs (which is defined in as.carbon) between two literal types must reside inside as.carbon itself.
    • Sized Conversions: Implementations targeting sized primitives (e.g. Int(N), Float(N)) must reside in their respective type source files (such as int.carbon or float.carbon) where the backing target type resides to prevent duplicate symbols and structural recursion loops.

High-Fidelity Validation & Test Authoring

Follow the Toolchain tests skill with specialized patterns for builtins:

1. Checker Builtin File Splits

Create validation splits under toolchain/check/testdata/builtins/:

  • Test Naming Convention: All tests under toolchain/check/testdata/builtins/ must be named after the builtin they are testing, replacing . characters in the builtin name with / (directories). For example, a test for the builtin "char_literal.convert" must be located at toolchain/check/testdata/builtins/char_literal/convert.carbon.
  • Minimal Prelude & Direct Call Isolation: Builtin tests must not test the prelude library or operators. They must use the minimal primitive prelude (// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon) or a smaller prelude, and explicitly declare and call the builtin functions under test directly (e.g., fn Add(a: f64, b: f64) -> f64 = "float.add";). This isolates the testing of compiler builtins from the library prelude.
  • Min-Prelude Limitations: Standard operators (like +, -, /, <, etc.) are not available in minimized preludes because the core operators library isn't imported. To write tests with a minimal footprint, call primitive builtins directly (e.g. float.negate, float.div) inside your test code to build expressions.
  • Canonicalized Float Comparison: In SemIR, real literal representations with identical mathematical values can result in mismatched RealId objects based on spelling variations. Verify compile-time constant conversions using canonicalized comparison functions (e.g. passing converted results through Expect(X as f64)) to completely avoid spelling mismatches in expected outputs.
  • Locals Bypass: If validating generic implicit conversions, compile-time arguments cannot take local runtime variable parameters. Validate compile-time conversions by passing literal constants directly, and sized variable implicit conversions at runtime.

2. Machine Codegen Lowering Splits

Create testing splits under toolchain/lower/testdata/builtins/:

  • Emplace a simple carbon binding to the tested builtin.
  • Confirm matching LLVM metadata target definitions are mapped precisely (e.g., matching sitofp i32 %a to float, fptosi float %a to i32).

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use this skill to read GitHub issue details and comments from the command line with `gh`. It covers issues referenced by number or URL, including issues in other repositories, and keeps access read-only so issue state is not changed.

  • Reading an issue and its full comment history
  • Opening an issue referenced by URL
  • Viewing an issue from another repository
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Agent tools

FreeSummary

Use this skill to guide an AI assistant working in the Carbon repository on viewing, searching, and editing files with specialized environment tools. It also sets rules for temporary files and when custom programs may be used.

  • Choosing approved tools to view, search, or edit files
  • Checking where temporary analysis and test files belong
  • Deciding when a custom transformation program is allowed
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Bazel usage

FreeSummary

Follow the Carbon project's Bazel workflow through Bazelisk, a version-managing launcher. Learn commands for building targets, running tests and binaries, enabling AddressSanitizer, and refreshing cached state after system dependency changes.

  • Building a Carbon target
  • Running focused project tests
  • Launching a Bazel-built binary
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Code style

FreeSummary

Apply Carbon toolchain conventions for formatting Bazel, C++, Carbon, Markdown, and Python files. The skill also covers clear comments and documentation, accurate names, design choices, and concise commit descriptions.

  • Formatting a changed source file
  • Writing or revising code comments
  • Choosing names that match their meaning
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Diagnostics

FreeSummary

Use this skill to add or revise diagnostics in the Carbon toolchain. It explains how to register message kinds, format typed arguments, attach source labels and context, and verify that diagnostics follow the project’s wording and test coverage rules.

  • Adding an error or warning to the Carbon compiler
  • Attaching source locations and explanatory labels
  • Checking diagnostic wording and formatting conventions
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

GitHub CLI usage

FreeSummary

Use `gh`, the GitHub command-line tool, to list, inspect, and search issues, pull requests, repositories, and code. The skill also explains read-only REST and GraphQL queries, pagination, and ways to filter or format returned data.

  • Checking an issue or pull request
  • Searching issues, code, or repositories
  • Querying GitHub data with the API
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

See all skills by carbon-language

Report a problem with this skill