From 3645143e27df415a10fad6d86a5e70ccd5d912e7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 16 Dec 2024 16:47:51 -0800 Subject: [PATCH] Add solutions for advent of code 2024 day 1 to `examples/`. (#4673) In order to support these examples, this adds two new builtins to the toolchain: `print.char` and `read.char`, which map to the libc functions `putchar` and `getchar`. --- core/BUILD | 3 +- core/io.carbon | 19 ++++ core/prelude.carbon | 4 - examples/advent2024/BUILD | 26 +++++ examples/advent2024/README.md | 17 +++ examples/advent2024/day1_common.carbon | 21 ++++ examples/advent2024/day1_part1.carbon | 26 +++++ examples/advent2024/day1_part2.carbon | 31 +++++ examples/advent2024/io_utils.carbon | 73 ++++++++++++ examples/advent2024/sort.carbon | 43 +++++++ examples/sieve.carbon | 2 + toolchain/check/eval.cpp | 7 +- .../check/testdata/builtins/print/char.carbon | 107 ++++++++++++++++++ .../{print.carbon => print/int.carbon} | 45 ++++---- .../check/testdata/builtins/read/int.carbon | 84 ++++++++++++++ toolchain/lower/handle_call.cpp | 50 ++++++-- .../lower/testdata/builtins/print.carbon | 39 ------- .../lower/testdata/builtins/print_read.carbon | 86 ++++++++++++++ toolchain/sem_ir/builtin_function_kind.cpp | 13 ++- toolchain/sem_ir/builtin_function_kind.def | 4 + 20 files changed, 620 insertions(+), 80 deletions(-) create mode 100644 core/io.carbon create mode 100644 examples/advent2024/BUILD create mode 100644 examples/advent2024/README.md create mode 100644 examples/advent2024/day1_common.carbon create mode 100644 examples/advent2024/day1_part1.carbon create mode 100644 examples/advent2024/day1_part2.carbon create mode 100644 examples/advent2024/io_utils.carbon create mode 100644 examples/advent2024/sort.carbon create mode 100644 toolchain/check/testdata/builtins/print/char.carbon rename toolchain/check/testdata/builtins/{print.carbon => print/int.carbon} (76%) create mode 100644 toolchain/check/testdata/builtins/read/int.carbon delete mode 100644 toolchain/lower/testdata/builtins/print.carbon create mode 100644 toolchain/lower/testdata/builtins/print_read.carbon diff --git a/core/BUILD b/core/BUILD index 4a1028d21de8..ad635c024239 100644 --- a/core/BUILD +++ b/core/BUILD @@ -5,9 +5,10 @@ load("//bazel/manifest:defs.bzl", "manifest") # Raw prelude files. +# TODO: This includes all of Core, not just the prelude. filegroup( name = "prelude_files", - srcs = ["prelude.carbon"] + glob(["prelude/**/*.carbon"]), + srcs = glob(["**/*.carbon"]), visibility = ["//visibility:public"], ) diff --git a/core/io.carbon b/core/io.carbon new file mode 100644 index 000000000000..80c1aa3acf05 --- /dev/null +++ b/core/io.carbon @@ -0,0 +1,19 @@ +// 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 +// +// AUTOUPDATE + +package Core library "io"; + +import library "prelude"; + +// TODO: Support printing other types. +// TODO: Consider rewriting using native support once library support exists. +fn Print(x: i32) = "print.int"; +fn PrintChar(x: i32) -> i32 = "print.char"; +fn ReadChar() -> i32 = "read.char"; + +// TODO: Change this to a global constant once they are fully supported. +// TODO: Use simply -1 once we support negate on an IntLiteral. +fn EOF() -> i32 { return -(1 as i32); } diff --git a/core/prelude.carbon b/core/prelude.carbon index 8e3969c3e012..a6a9e2a9d7a2 100644 --- a/core/prelude.carbon +++ b/core/prelude.carbon @@ -8,7 +8,3 @@ package Core library "prelude"; export import library "prelude/operators"; export import library "prelude/types"; - -// TODO: Support printing other types. -// TODO: Consider rewriting using native support once library support exists. -fn Print(x: i32) = "print.int"; diff --git a/examples/advent2024/BUILD b/examples/advent2024/BUILD new file mode 100644 index 000000000000..c38b1cb61929 --- /dev/null +++ b/examples/advent2024/BUILD @@ -0,0 +1,26 @@ +# 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 + +load("//bazel/carbon_rules:defs.bzl", "carbon_binary") + +utils = [ + "io_utils.carbon", + "sort.carbon", +] + +carbon_binary( + name = "day1_part1", + srcs = [ + "day1_common.carbon", + "day1_part1.carbon", + ] + utils, +) + +carbon_binary( + name = "day1_part2", + srcs = [ + "day1_common.carbon", + "day1_part2.carbon", + ] + utils, +) diff --git a/examples/advent2024/README.md b/examples/advent2024/README.md new file mode 100644 index 000000000000..f7bdef6fbd35 --- /dev/null +++ b/examples/advent2024/README.md @@ -0,0 +1,17 @@ +# Advent of Code 2024 + + + +This directory contains sample solutions written in Carbon for +[Advent of Code 2024](https://adventofcode.com/2024/). + +The Carbon toolchain is in a very early state, so these samples frequently need +to work around missing functionality and are not reflective of expected Carbon +style and idioms. Instead, the purpose of these examples are to test the current +state of the toolchain against larger code examples than those that are present +in the toolchain's own tests, to find bugs in the toolchain, and to drive +feature development in the toolchain by presenting somewhat realistic testcases. diff --git a/examples/advent2024/day1_common.carbon b/examples/advent2024/day1_common.carbon new file mode 100644 index 000000000000..b4c1c21295ee --- /dev/null +++ b/examples/advent2024/day1_common.carbon @@ -0,0 +1,21 @@ +// 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 + +library "day1_common"; + +import library "io_utils"; + +// Read a sequence of lines each containing a pair of numbers into two arrays. +// Returns the number of lines read. +fn ReadInputs(ap: [i32; 1000]*, bp: [i32; 1000]*) -> i32 { + var n: i32 = 0; + var a: i32; + var b: i32; + while (n < 1000 and ReadInt(&a) and SkipSpaces() and ReadInt(&b) and SkipNewline()) { + (*ap)[n] = a; + (*bp)[n] = b; + ++n; + } + return n; +} diff --git a/examples/advent2024/day1_part1.carbon b/examples/advent2024/day1_part1.carbon new file mode 100644 index 000000000000..b1d1f411e8aa --- /dev/null +++ b/examples/advent2024/day1_part1.carbon @@ -0,0 +1,26 @@ +// 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 + +import Core library "io"; + +import library "day1_common"; +import library "sort"; + +fn Abs(n: i32) -> i32 { return if n < 0 then -n else n; } + +fn Run() { + var a: [i32; 1000]; + var b: [i32; 1000]; + var n: i32 = ReadInputs(&a, &b); + Quicksort(&a, 0, n); + Quicksort(&b, 0, n); + + var i: i32 = 0; + var difference: i32 = 0; + while (i < n) { + difference += Abs(a[i] - b[i]); + i += 1; + } + Core.Print(difference); +} diff --git a/examples/advent2024/day1_part2.carbon b/examples/advent2024/day1_part2.carbon new file mode 100644 index 000000000000..c6521822a8ec --- /dev/null +++ b/examples/advent2024/day1_part2.carbon @@ -0,0 +1,31 @@ +// 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 + +import Core library "io"; + +import library "day1_common"; +import library "sort"; + +fn Run() { + var a: [i32; 1000]; + var b: [i32; 1000]; + var n: i32 = ReadInputs(&a, &b); + Quicksort(&a, 0, n); + Quicksort(&b, 0, n); + + var i: i32 = 0; + var j: i32 = 0; + var similarity: i32 = 0; + while (i < n and j < n) { + if (a[i] < b[j]) { + ++i; + } else { + if (a[i] == b[j]) { + similarity += b[j]; + } + ++j; + } + } + Core.Print(similarity); +} diff --git a/examples/advent2024/io_utils.carbon b/examples/advent2024/io_utils.carbon new file mode 100644 index 000000000000..ce26cbcfe7e6 --- /dev/null +++ b/examples/advent2024/io_utils.carbon @@ -0,0 +1,73 @@ +// 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 + +library "io_utils"; + +import Core library "io"; + +// TODO: Use Core.EOF() rather than 0 as a sentinel here. +// At the moment, 0 is the only value we can initialize a global to, +// so we store the value plus 1 here. +var push_back: i32 = 0; + +fn ReadChar() -> i32 { + var next: i32 = push_back - 1; + push_back = 0; + // TODO: assert(push_back == Core.EOF()); + if (next == Core.EOF()) { + next = Core.ReadChar(); + } + return next; +} + +fn UnreadChar(c: i32) { + // TODO: assert(push_back == Core.EOF()); + push_back = c + 1; +} + +fn ReadInt(p: i32*) -> bool { + var read_any_digits: bool = false; + *p = 0; + + while (true) { + var c: i32 = ReadChar(); + if (c < 0x30 or c > 0x39) { + UnreadChar(c); + break; + } + // TODO: Check for overflow. + *p *= 10; + *p += c - 0x30; + read_any_digits = true; + } + return read_any_digits; +} + +fn SkipSpaces() -> bool { + var skipped_any_spaces: bool = false; + while (true) { + var c: i32 = ReadChar(); + if (c != 0x20) { + UnreadChar(c); + break; + } + skipped_any_spaces = true; + } + return skipped_any_spaces; +} + +fn SkipNewline() -> bool { + var c: i32 = ReadChar(); + // Optional carriage return. + if (c == 0x0D) { + c = ReadChar(); + } + // Newline. + if (c == 0x0A) { + return true; + } + // TODO: Unread the CR? + UnreadChar(c); + return false; +} diff --git a/examples/advent2024/sort.carbon b/examples/advent2024/sort.carbon new file mode 100644 index 000000000000..c0be66c5a5da --- /dev/null +++ b/examples/advent2024/sort.carbon @@ -0,0 +1,43 @@ +// 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 + +library "sort"; + +// TODO: Generalize this for other container types once we implement lowering +// for generic functions. + +fn Swap(p: [i32; 1000]*, from: i32, to: i32) { + var tmp: i32 = (*p)[from]; + (*p)[from] = (*p)[to]; + (*p)[to] = tmp; +} + +fn Partition(p: [i32; 1000]*, from_in: i32, to_in: i32) -> i32 { + var pivot_index: i32 = from_in; + var pivot: i32 = (*p)[pivot_index]; + var from: i32 = from_in + 1; + var to: i32 = to_in; + while (from < to) { + if ((*p)[from] <= pivot) { + ++from; + } else if ((*p)[to - 1] > pivot) { + --to; + } else { + // Element at `from` is > pivot, and + // element at `to` is <= pivot. + Swap(p, from, to - 1); + ++from; + --to; + } + } + Swap(p, pivot_index, from - 1); + return from - 1; +} + +fn Quicksort(p: [i32; 1000]*, from: i32, to: i32) { + if (from + 1 >= to) { return; } + var pivot: i32 = Partition(p, from, to); + Quicksort(p, from, pivot); + Quicksort(p, pivot + 1, to); +} diff --git a/examples/sieve.carbon b/examples/sieve.carbon index c882d7581003..9fddf037330b 100644 --- a/examples/sieve.carbon +++ b/examples/sieve.carbon @@ -2,6 +2,8 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +import Core library "io"; + // Compute and return the number of primes less than 1000. class Sieve { diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 489e34057c4f..27dae45cc681 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -1107,8 +1107,11 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc, case SemIR::BuiltinFunctionKind::None: CARBON_FATAL("Not a builtin function."); - case SemIR::BuiltinFunctionKind::PrintInt: { - // Providing a constant result would allow eliding the function call. + case SemIR::BuiltinFunctionKind::PrintChar: + case SemIR::BuiltinFunctionKind::PrintInt: + case SemIR::BuiltinFunctionKind::ReadChar: { + // These are runtime-only builtins. + // TODO: Consider tracking this on the `BuiltinFunctionKind`. return SemIR::ConstantId::NotConstant; } diff --git a/toolchain/check/testdata/builtins/print/char.carbon b/toolchain/check/testdata/builtins/print/char.carbon new file mode 100644 index 000000000000..1eb168d79e52 --- /dev/null +++ b/toolchain/check/testdata/builtins/print/char.carbon @@ -0,0 +1,107 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/print/char.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/print/char.carbon + +import Core library "io"; + +fn PrintChar(a: i32) -> i32 = "print.char"; + +fn Main() { + PrintChar(1); + Core.PrintChar(2); +} + +// CHECK:STDOUT: --- char.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template] +// CHECK:STDOUT: %PrintChar.type.1: type = fn_type @PrintChar.1 [template] +// CHECK:STDOUT: %PrintChar.1: %PrintChar.type.1 = struct_value () [template] +// CHECK:STDOUT: %Main.type: type = fn_type @Main [template] +// CHECK:STDOUT: %Main: %Main.type = struct_value () [template] +// CHECK:STDOUT: %int_1.1: Core.IntLiteral = int_value 1 [template] +// CHECK:STDOUT: %Convert.type.2: type = fn_type @Convert.1, @ImplicitAs(%i32) [template] +// CHECK:STDOUT: %Convert.type.10: type = fn_type @Convert.2, @impl.1(%int_32) [template] +// CHECK:STDOUT: %Convert.10: %Convert.type.10 = struct_value () [template] +// CHECK:STDOUT: %interface.5: = interface_witness (%Convert.10) [template] +// CHECK:STDOUT: %Convert.bound.1: = bound_method %int_1.1, %Convert.10 [template] +// CHECK:STDOUT: %Convert.specific_fn.1: = specific_function %Convert.bound.1, @Convert.2(%int_32) [template] +// CHECK:STDOUT: %int_1.2: %i32 = int_value 1 [template] +// CHECK:STDOUT: %PrintChar.type.2: type = fn_type @PrintChar.2 [template] +// CHECK:STDOUT: %PrintChar.2: %PrintChar.type.2 = struct_value () [template] +// CHECK:STDOUT: %int_2.1: Core.IntLiteral = int_value 2 [template] +// CHECK:STDOUT: %Convert.bound.2: = bound_method %int_2.1, %Convert.10 [template] +// CHECK:STDOUT: %Convert.specific_fn.2: = specific_function %Convert.bound.2, @Convert.2(%int_32) [template] +// CHECK:STDOUT: %int_2.2: %i32 = int_value 2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Int = %import_ref.1 +// CHECK:STDOUT: .ImplicitAs = %import_ref.5 +// CHECK:STDOUT: .PrintChar = %import_ref.193 +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//io +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.193: %PrintChar.type.2 = import_ref Core//io, PrintChar, loaded [template = constants.%PrintChar.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .PrintChar = %PrintChar.decl +// CHECK:STDOUT: .Main = %Main.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %PrintChar.decl: %PrintChar.type.1 = fn_decl @PrintChar.1 [template = constants.%PrintChar.1] { +// CHECK:STDOUT: %a.patt: %i32 = binding_pattern a +// CHECK:STDOUT: %a.param_patt: %i32 = value_param_pattern %a.patt, runtime_param0 +// CHECK:STDOUT: %return.patt: %i32 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %i32 = out_param_pattern %return.patt, runtime_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %int_32.loc13_17: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %i32.loc13_17: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32] +// CHECK:STDOUT: %int_32.loc13_25: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %i32.loc13_25: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32] +// CHECK:STDOUT: %a.param: %i32 = value_param runtime_param0 +// CHECK:STDOUT: %a: %i32 = bind_name a, %a.param +// CHECK:STDOUT: %return.param: ref %i32 = out_param runtime_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [template = constants.%Main] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @PrintChar.1(%a.param_patt: %i32) -> %i32 = "print.char"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Main() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %PrintChar.ref.loc16: %PrintChar.type.1 = name_ref PrintChar, file.%PrintChar.decl [template = constants.%PrintChar.1] +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [template = constants.%int_1.1] +// CHECK:STDOUT: %impl.elem0.loc16: %Convert.type.2 = interface_witness_access constants.%interface.5, element0 [template = constants.%Convert.10] +// CHECK:STDOUT: %Convert.bound.loc16: = bound_method %int_1, %impl.elem0.loc16 [template = constants.%Convert.bound.1] +// CHECK:STDOUT: %Convert.specific_fn.loc16: = specific_function %Convert.bound.loc16, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.1] +// CHECK:STDOUT: %int.convert_checked.loc16: init %i32 = call %Convert.specific_fn.loc16(%int_1) [template = constants.%int_1.2] +// CHECK:STDOUT: %.loc16_13.1: %i32 = value_of_initializer %int.convert_checked.loc16 [template = constants.%int_1.2] +// CHECK:STDOUT: %.loc16_13.2: %i32 = converted %int_1, %.loc16_13.1 [template = constants.%int_1.2] +// CHECK:STDOUT: %print.char.loc16: init %i32 = call %PrintChar.ref.loc16(%.loc16_13.2) +// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] +// CHECK:STDOUT: %PrintChar.ref.loc17: %PrintChar.type.2 = name_ref PrintChar, imports.%import_ref.193 [template = constants.%PrintChar.2] +// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [template = constants.%int_2.1] +// CHECK:STDOUT: %impl.elem0.loc17: %Convert.type.2 = interface_witness_access constants.%interface.5, element0 [template = constants.%Convert.10] +// CHECK:STDOUT: %Convert.bound.loc17: = bound_method %int_2, %impl.elem0.loc17 [template = constants.%Convert.bound.2] +// CHECK:STDOUT: %Convert.specific_fn.loc17: = specific_function %Convert.bound.loc17, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.2] +// CHECK:STDOUT: %int.convert_checked.loc17: init %i32 = call %Convert.specific_fn.loc17(%int_2) [template = constants.%int_2.2] +// CHECK:STDOUT: %.loc17_18.1: %i32 = value_of_initializer %int.convert_checked.loc17 [template = constants.%int_2.2] +// CHECK:STDOUT: %.loc17_18.2: %i32 = converted %int_2, %.loc17_18.1 [template = constants.%int_2.2] +// CHECK:STDOUT: %print.char.loc17: init %i32 = call %PrintChar.ref.loc17(%.loc17_18.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/print.carbon b/toolchain/check/testdata/builtins/print/int.carbon similarity index 76% rename from toolchain/check/testdata/builtins/print.carbon rename to toolchain/check/testdata/builtins/print/int.carbon index d321a0039bb4..4b24230f4526 100644 --- a/toolchain/check/testdata/builtins/print.carbon +++ b/toolchain/check/testdata/builtins/print/int.carbon @@ -4,9 +4,11 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/print.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/print/int.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/print.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/print/int.carbon + +import Core library "io"; fn Print(a: i32) = "print.int"; @@ -16,7 +18,7 @@ fn Main() { Core.Print(2); } -// CHECK:STDOUT: --- print.carbon +// CHECK:STDOUT: --- int.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template] @@ -48,9 +50,10 @@ fn Main() { // CHECK:STDOUT: .ImplicitAs = %import_ref.5 // CHECK:STDOUT: .Print = %import_ref.193 // CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//io // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.193: %Print.type.2 = import_ref Core//prelude, Print, loaded [template = constants.%Print.2] +// CHECK:STDOUT: %import_ref.193: %Print.type.2 = import_ref Core//io, Print, loaded [template = constants.%Print.2] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -76,25 +79,25 @@ fn Main() { // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Print.ref.loc14: %Print.type.1 = name_ref Print, file.%Print.decl [template = constants.%Print.1] +// CHECK:STDOUT: %Print.ref.loc16: %Print.type.1 = name_ref Print, file.%Print.decl [template = constants.%Print.1] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [template = constants.%int_1.1] -// CHECK:STDOUT: %impl.elem0.loc14: %Convert.type.2 = interface_witness_access constants.%interface.5, element0 [template = constants.%Convert.10] -// CHECK:STDOUT: %Convert.bound.loc14: = bound_method %int_1, %impl.elem0.loc14 [template = constants.%Convert.bound.1] -// CHECK:STDOUT: %Convert.specific_fn.loc14: = specific_function %Convert.bound.loc14, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.1] -// CHECK:STDOUT: %int.convert_checked.loc14: init %i32 = call %Convert.specific_fn.loc14(%int_1) [template = constants.%int_1.2] -// CHECK:STDOUT: %.loc14_9.1: %i32 = value_of_initializer %int.convert_checked.loc14 [template = constants.%int_1.2] -// CHECK:STDOUT: %.loc14_9.2: %i32 = converted %int_1, %.loc14_9.1 [template = constants.%int_1.2] -// CHECK:STDOUT: %print.int.loc14: init %empty_tuple.type = call %Print.ref.loc14(%.loc14_9.2) -// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] -// CHECK:STDOUT: %Print.ref.loc16: %Print.type.2 = name_ref Print, imports.%import_ref.193 [template = constants.%Print.2] -// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [template = constants.%int_2.1] // CHECK:STDOUT: %impl.elem0.loc16: %Convert.type.2 = interface_witness_access constants.%interface.5, element0 [template = constants.%Convert.10] -// CHECK:STDOUT: %Convert.bound.loc16: = bound_method %int_2, %impl.elem0.loc16 [template = constants.%Convert.bound.2] -// CHECK:STDOUT: %Convert.specific_fn.loc16: = specific_function %Convert.bound.loc16, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.2] -// CHECK:STDOUT: %int.convert_checked.loc16: init %i32 = call %Convert.specific_fn.loc16(%int_2) [template = constants.%int_2.2] -// CHECK:STDOUT: %.loc16_14.1: %i32 = value_of_initializer %int.convert_checked.loc16 [template = constants.%int_2.2] -// CHECK:STDOUT: %.loc16_14.2: %i32 = converted %int_2, %.loc16_14.1 [template = constants.%int_2.2] -// CHECK:STDOUT: %print.int.loc16: init %empty_tuple.type = call %Print.ref.loc16(%.loc16_14.2) +// CHECK:STDOUT: %Convert.bound.loc16: = bound_method %int_1, %impl.elem0.loc16 [template = constants.%Convert.bound.1] +// CHECK:STDOUT: %Convert.specific_fn.loc16: = specific_function %Convert.bound.loc16, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.1] +// CHECK:STDOUT: %int.convert_checked.loc16: init %i32 = call %Convert.specific_fn.loc16(%int_1) [template = constants.%int_1.2] +// CHECK:STDOUT: %.loc16_9.1: %i32 = value_of_initializer %int.convert_checked.loc16 [template = constants.%int_1.2] +// CHECK:STDOUT: %.loc16_9.2: %i32 = converted %int_1, %.loc16_9.1 [template = constants.%int_1.2] +// CHECK:STDOUT: %print.int.loc16: init %empty_tuple.type = call %Print.ref.loc16(%.loc16_9.2) +// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] +// CHECK:STDOUT: %Print.ref.loc18: %Print.type.2 = name_ref Print, imports.%import_ref.193 [template = constants.%Print.2] +// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [template = constants.%int_2.1] +// CHECK:STDOUT: %impl.elem0.loc18: %Convert.type.2 = interface_witness_access constants.%interface.5, element0 [template = constants.%Convert.10] +// CHECK:STDOUT: %Convert.bound.loc18: = bound_method %int_2, %impl.elem0.loc18 [template = constants.%Convert.bound.2] +// CHECK:STDOUT: %Convert.specific_fn.loc18: = specific_function %Convert.bound.loc18, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.2] +// CHECK:STDOUT: %int.convert_checked.loc18: init %i32 = call %Convert.specific_fn.loc18(%int_2) [template = constants.%int_2.2] +// CHECK:STDOUT: %.loc18_14.1: %i32 = value_of_initializer %int.convert_checked.loc18 [template = constants.%int_2.2] +// CHECK:STDOUT: %.loc18_14.2: %i32 = converted %int_2, %.loc18_14.1 [template = constants.%int_2.2] +// CHECK:STDOUT: %print.int.loc18: init %empty_tuple.type = call %Print.ref.loc18(%.loc18_14.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/read/int.carbon b/toolchain/check/testdata/builtins/read/int.carbon new file mode 100644 index 000000000000..29cb202acf33 --- /dev/null +++ b/toolchain/check/testdata/builtins/read/int.carbon @@ -0,0 +1,84 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/read/int.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/read/int.carbon + +import Core library "io"; + +fn ReadChar() -> i32 = "read.char"; + +fn Main() { + let n: i32 = ReadChar(); + let m: i32 = Core.ReadChar(); +} + +// CHECK:STDOUT: --- int.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template] +// CHECK:STDOUT: %ReadChar.type.1: type = fn_type @ReadChar.1 [template] +// CHECK:STDOUT: %ReadChar.1: %ReadChar.type.1 = struct_value () [template] +// CHECK:STDOUT: %Main.type: type = fn_type @Main [template] +// CHECK:STDOUT: %Main: %Main.type = struct_value () [template] +// CHECK:STDOUT: %ReadChar.type.2: type = fn_type @ReadChar.2 [template] +// CHECK:STDOUT: %ReadChar.2: %ReadChar.type.2 = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Int = %import_ref.1 +// CHECK:STDOUT: .ReadChar = %import_ref.5 +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//io +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.5: %ReadChar.type.2 = import_ref Core//io, ReadChar, loaded [template = constants.%ReadChar.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .ReadChar = %ReadChar.decl +// CHECK:STDOUT: .Main = %Main.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %ReadChar.decl: %ReadChar.type.1 = fn_decl @ReadChar.1 [template = constants.%ReadChar.1] { +// CHECK:STDOUT: %return.patt: %i32 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %i32 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32] +// CHECK:STDOUT: %return.param: ref %i32 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [template = constants.%Main] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @ReadChar.1() -> %i32 = "read.char"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Main() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_32.loc16: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %i32.loc16: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32] +// CHECK:STDOUT: %ReadChar.ref.loc16: %ReadChar.type.1 = name_ref ReadChar, file.%ReadChar.decl [template = constants.%ReadChar.1] +// CHECK:STDOUT: %read.char.loc16: init %i32 = call %ReadChar.ref.loc16() +// CHECK:STDOUT: %.loc16_26.1: %i32 = value_of_initializer %read.char.loc16 +// CHECK:STDOUT: %.loc16_26.2: %i32 = converted %read.char.loc16, %.loc16_26.1 +// CHECK:STDOUT: %n: %i32 = bind_name n, %.loc16_26.2 +// CHECK:STDOUT: %int_32.loc17: Core.IntLiteral = int_value 32 [template = constants.%int_32] +// CHECK:STDOUT: %i32.loc17: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32] +// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] +// CHECK:STDOUT: %ReadChar.ref.loc17: %ReadChar.type.2 = name_ref ReadChar, imports.%import_ref.5 [template = constants.%ReadChar.2] +// CHECK:STDOUT: %read.char.loc17: init %i32 = call %ReadChar.ref.loc17() +// CHECK:STDOUT: %.loc17_31.1: %i32 = value_of_initializer %read.char.loc17 +// CHECK:STDOUT: %.loc17_31.2: %i32 = converted %read.char.loc17, %.loc17_31.1 +// CHECK:STDOUT: %m: %i32 = bind_name m, %.loc17_31.2 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index e738becf055e..f3af55cb769c 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -76,20 +76,48 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::None: CARBON_FATAL("No callee in function call."); + case SemIR::BuiltinFunctionKind::PrintChar: { + auto* i32_type = llvm::IntegerType::getInt32Ty(context.llvm_context()); + llvm::Value* arg_value = context.builder().CreateSExtOrTrunc( + context.GetValue(arg_ids[0]), i32_type); + auto putchar = context.llvm_module().getOrInsertFunction( + "putchar", i32_type, i32_type); + auto* result = context.builder().CreateCall(putchar, {arg_value}); + context.SetLocal( + inst_id, + context.builder().CreateSExtOrTrunc( + result, context.GetType( + context.sem_ir().insts().Get(inst_id).type_id()))); + return; + } + case SemIR::BuiltinFunctionKind::PrintInt: { - llvm::Type* char_type[] = {llvm::PointerType::get( - llvm::Type::getInt8Ty(context.llvm_context()), 0)}; - auto* printf_type = llvm::FunctionType::get( - llvm::IntegerType::getInt32Ty(context.llvm_context()), - llvm::ArrayRef(char_type, 1), /*isVarArg=*/true); - auto callee = + auto* i32_type = llvm::IntegerType::getInt32Ty(context.llvm_context()); + auto* ptr_type = llvm::PointerType::get(context.llvm_context(), 0); + auto* printf_type = llvm::FunctionType::get(i32_type, {ptr_type}, + /*isVarArg=*/true); + llvm::FunctionCallee printf = context.llvm_module().getOrInsertFunction("printf", printf_type); - llvm::SmallVector args = { - context.builder().CreateGlobalString("%d\n", "printf.int.format")}; - args.push_back(context.GetValue(arg_ids[0])); - context.SetLocal(inst_id, - context.builder().CreateCall(callee, args, "printf")); + llvm::Value* format_string = + context.builder().CreateGlobalString("%d\n", "printf.int.format"); + llvm::Value* arg_value = context.builder().CreateSExtOrTrunc( + context.GetValue(arg_ids[0]), i32_type); + context.SetLocal(inst_id, context.builder().CreateCall( + printf, {format_string, arg_value})); + return; + } + + case SemIR::BuiltinFunctionKind::ReadChar: { + auto* i32_type = llvm::IntegerType::getInt32Ty(context.llvm_context()); + auto getchar = + context.llvm_module().getOrInsertFunction("getchar", i32_type); + auto* result = context.builder().CreateCall(getchar, {}); + context.SetLocal( + inst_id, + context.builder().CreateSExtOrTrunc( + result, context.GetType( + context.sem_ir().insts().Get(inst_id).type_id()))); return; } diff --git a/toolchain/lower/testdata/builtins/print.carbon b/toolchain/lower/testdata/builtins/print.carbon deleted file mode 100644 index b3666bc72949..000000000000 --- a/toolchain/lower/testdata/builtins/print.carbon +++ /dev/null @@ -1,39 +0,0 @@ -// 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 -// -// AUTOUPDATE -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/builtins/print.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/builtins/print.carbon - -fn Main() { - Core.Print(1); -} - -// CHECK:STDOUT: ; ModuleID = 'print.carbon' -// CHECK:STDOUT: source_filename = "print.carbon" -// CHECK:STDOUT: -// CHECK:STDOUT: @printf.int.format = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 -// CHECK:STDOUT: -// CHECK:STDOUT: define void @_CMain.Main() !dbg !4 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: %print.int.printf = call i32 (ptr, ...) @printf(ptr @printf.int.format, i32 1), !dbg !7 -// CHECK:STDOUT: ret void, !dbg !8 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: declare i32 @printf(ptr, ...) -// CHECK:STDOUT: -// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} -// CHECK:STDOUT: !llvm.dbg.cu = !{!2} -// CHECK:STDOUT: -// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} -// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} -// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) -// CHECK:STDOUT: !3 = !DIFile(filename: "print.carbon", directory: "") -// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) -// CHECK:STDOUT: !6 = !{} -// CHECK:STDOUT: !7 = !DILocation(line: 12, column: 3, scope: !4) -// CHECK:STDOUT: !8 = !DILocation(line: 11, column: 1, scope: !4) diff --git a/toolchain/lower/testdata/builtins/print_read.carbon b/toolchain/lower/testdata/builtins/print_read.carbon new file mode 100644 index 000000000000..8834eea2d519 --- /dev/null +++ b/toolchain/lower/testdata/builtins/print_read.carbon @@ -0,0 +1,86 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/builtins/print_read.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/builtins/print_read.carbon + +import Core library "io"; + +fn PrintChar(c: i8) -> i32 = "print.char"; +fn ReadChar() -> i32 = "read.char"; + +fn Main() { + Core.Print(1); + + let EOF: i32 = -(1 as i32); + while (ReadChar() != EOF) { + // "Hi" + if (PrintChar(0x48) != EOF) { + PrintChar(0x69); + } + } +} + +// CHECK:STDOUT: ; ModuleID = 'print_read.carbon' +// CHECK:STDOUT: source_filename = "print_read.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @printf.int.format = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CMain.Main() !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %print.int = call i32 (ptr, ...) @printf(ptr @printf.int.format, i32 1), !dbg !7 +// CHECK:STDOUT: br label %while.cond, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: while.cond: ; preds = %if.else, %entry +// CHECK:STDOUT: %read.char = call i32 @getchar(), !dbg !9 +// CHECK:STDOUT: %int.neq.loc20 = icmp ne i32 %read.char, -1, !dbg !9 +// CHECK:STDOUT: br i1 %int.neq.loc20, label %while.body, label %while.done, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: while.body: ; preds = %while.cond +// CHECK:STDOUT: %print.char.loc22 = call i32 @putchar(i32 72), !dbg !10 +// CHECK:STDOUT: %int.neq.loc22 = icmp ne i32 %print.char.loc22, -1, !dbg !10 +// CHECK:STDOUT: br i1 %int.neq.loc22, label %if.then, label %if.else, !dbg !11 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %while.body +// CHECK:STDOUT: %print.char.loc23 = call i32 @putchar(i32 105), !dbg !12 +// CHECK:STDOUT: br label %if.else, !dbg !13 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %if.then, %while.body +// CHECK:STDOUT: br label %while.cond, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: while.done: ; preds = %while.cond +// CHECK:STDOUT: ret void, !dbg !15 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @printf(ptr, ...) +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @getchar() +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @putchar(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @putchar, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "print_read.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Main", linkageName: "_CMain.Main", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 17, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 20, column: 9, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 20, column: 10, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 22, column: 9, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 22, column: 8, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 23, column: 7, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 22, column: 5, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 20, column: 3, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 16, column: 1, scope: !4) diff --git a/toolchain/sem_ir/builtin_function_kind.cpp b/toolchain/sem_ir/builtin_function_kind.cpp index 2f60dc154675..915c9cc9d6af 100644 --- a/toolchain/sem_ir/builtin_function_kind.cpp +++ b/toolchain/sem_ir/builtin_function_kind.cpp @@ -63,7 +63,8 @@ struct BuiltinType { } }; -// Constraint that the function has no return. +// Constraint that a type is `()`, used as the return type of builtin functions +// with no return value. struct NoReturn { static auto Check(const File& sem_ir, ValidateState& /*state*/, TypeId type_id) -> bool { @@ -194,10 +195,18 @@ using FloatT = TypeParam<0, AnyFloat>; // Not a builtin function. constexpr BuiltinInfo None = {"", nullptr}; -// Prints an argument. +// Prints a single character. +constexpr BuiltinInfo PrintChar = {"print.char", + ValidateSignatureAnyInt>}; + +// Prints an integer. constexpr BuiltinInfo PrintInt = {"print.int", ValidateSignatureNoReturn>}; +// Reads a single character from stdin. +constexpr BuiltinInfo ReadChar = {"read.char", + ValidateSignatureAnyInt>}; + // Returns the `Core.IntLiteral` type. constexpr BuiltinInfo IntLiteralMakeType = {"int_literal.make_type", ValidateSignatureType>}; diff --git a/toolchain/sem_ir/builtin_function_kind.def b/toolchain/sem_ir/builtin_function_kind.def index 439ded7df887..d74d755da45f 100644 --- a/toolchain/sem_ir/builtin_function_kind.def +++ b/toolchain/sem_ir/builtin_function_kind.def @@ -17,7 +17,11 @@ #endif CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(None) + +// Temporary builtins for primitive IO. +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(PrintChar) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(PrintInt) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(ReadChar) // Type factories. CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntLiteralMakeType)