mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:20:13 +01:00
Add a basic Core.Print function for ints. (#4078)
We'd been discussing that explorer remains necessary for print, and I was wondering if this kind of approach would be okay (we _probably_ want this to work, based on #2110, albeit with more overloads -- but I don't think there's a good way to support overloads at the moment). ``` ╚╡../bazel-bin/examples/sieve 2 3 5 7 11 13 17 19 23 29 31 37 41 43 ... ```
This commit is contained in:
@@ -8,3 +8,7 @@ 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";
|
||||
|
||||
@@ -116,6 +116,7 @@ fn Run() -> i32 {
|
||||
while (n < 1000) {
|
||||
if (s.is_prime[n]) {
|
||||
++number_of_primes;
|
||||
Core.Print(n);
|
||||
s.MarkMultiplesNotPrime(n);
|
||||
}
|
||||
++n;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "toolchain/sem_ir/builtin_function_kind.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/inst_kind.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
@@ -709,6 +710,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.
|
||||
return SemIR::ConstantId::NotConstant;
|
||||
}
|
||||
|
||||
case SemIR::BuiltinFunctionKind::IntMakeType32: {
|
||||
return context.constant_values().Get(SemIR::InstId::BuiltinIntType);
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// 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.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/print.carbon
|
||||
|
||||
fn Print(a: i32) = "print.int";
|
||||
|
||||
fn Main() {
|
||||
Print(1);
|
||||
|
||||
Core.Print(2);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- print.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %Int32.type: type = fn_type @Int32 [template]
|
||||
// CHECK:STDOUT: %.1: type = tuple_type () [template]
|
||||
// CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template]
|
||||
// CHECK:STDOUT: %Print.type.1: type = fn_type @Print.1 [template]
|
||||
// CHECK:STDOUT: %Print.1: %Print.type.1 = struct_value () [template]
|
||||
// CHECK:STDOUT: %Main.type: type = fn_type @Main [template]
|
||||
// CHECK:STDOUT: %Main: %Main.type = struct_value () [template]
|
||||
// CHECK:STDOUT: %.2: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %Print.type.2: type = fn_type @Print.2 [template]
|
||||
// CHECK:STDOUT: %Print.2: %Print.type.2 = struct_value () [template]
|
||||
// CHECK:STDOUT: %.3: i32 = int_literal 2 [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [template] {
|
||||
// CHECK:STDOUT: .Core = %Core
|
||||
// CHECK:STDOUT: .Print = %Print.decl
|
||||
// CHECK:STDOUT: .Main = %Main.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core: <namespace> = namespace [template] {}
|
||||
// CHECK:STDOUT: %import_ref.1: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
|
||||
// CHECK:STDOUT: %Print.decl: %Print.type.1 = fn_decl @Print.1 [template = constants.%Print.1] {
|
||||
// CHECK:STDOUT: %int.make_type_32: init type = call constants.%Int32() [template = i32]
|
||||
// CHECK:STDOUT: %.loc11_13.1: type = value_of_initializer %int.make_type_32 [template = i32]
|
||||
// CHECK:STDOUT: %.loc11_13.2: type = converted %int.make_type_32, %.loc11_13.1 [template = i32]
|
||||
// CHECK:STDOUT: %a.loc11_10.1: i32 = param a
|
||||
// CHECK:STDOUT: @Print.1.%a: i32 = bind_name a, %a.loc11_10.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [template = constants.%Main] {}
|
||||
// CHECK:STDOUT: %import_ref.2: %Print.type.2 = import_ref ir1, inst+42, loaded [template = constants.%Print.2]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Print.1(%a: i32) = "print.int";
|
||||
// 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: %.loc14: i32 = int_literal 1 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %print.int.loc14: init %.1 = call %Print.ref.loc14(%.loc14)
|
||||
// CHECK:STDOUT: %Core.ref: <namespace> = name_ref Core, file.%Core [template = file.%Core]
|
||||
// CHECK:STDOUT: %Print.ref.loc16: %Print.type.2 = name_ref Print, file.%import_ref.2 [template = constants.%Print.2]
|
||||
// CHECK:STDOUT: %.loc16: i32 = int_literal 2 [template = constants.%.3]
|
||||
// CHECK:STDOUT: %print.int.loc16: init %.1 = call %Print.ref.loc16(%.loc16)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Print.2(%x: i32) = "print.int";
|
||||
// CHECK:STDOUT:
|
||||
@@ -29,10 +29,9 @@ auto CodeGen::Make(llvm::Module& module, llvm::StringRef target_triple,
|
||||
constexpr llvm::StringLiteral Features = "";
|
||||
|
||||
llvm::TargetOptions target_opts;
|
||||
std::optional<llvm::Reloc::Model> reloc_model;
|
||||
CodeGen codegen(module, errors);
|
||||
codegen.target_machine_.reset(target->createTargetMachine(
|
||||
target_triple, CPU, Features, target_opts, reloc_model));
|
||||
target_triple, CPU, Features, target_opts, llvm::Reloc::PIC_));
|
||||
return codegen;
|
||||
}
|
||||
|
||||
|
||||
@@ -235,6 +235,23 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id,
|
||||
case SemIR::BuiltinFunctionKind::None:
|
||||
CARBON_FATAL() << "No callee in function call.";
|
||||
|
||||
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<llvm::Type*>(char_type, 1), /*isVarArg=*/true);
|
||||
auto callee =
|
||||
context.llvm_module().getOrInsertFunction("printf", printf_type);
|
||||
|
||||
llvm::SmallVector<llvm::Value*, 1> 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"));
|
||||
return;
|
||||
}
|
||||
|
||||
case SemIR::BuiltinFunctionKind::BoolMakeType:
|
||||
case SemIR::BuiltinFunctionKind::FloatMakeType:
|
||||
case SemIR::BuiltinFunctionKind::IntMakeType32:
|
||||
|
||||
+26
@@ -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
|
||||
//
|
||||
// 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 @Main() {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %print.int.printf = call i32 (ptr, ...) @printf(ptr @printf.int.format, i32 1)
|
||||
// CHECK:STDOUT: ret void
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: declare i32 @printf(ptr, ...)
|
||||
@@ -60,6 +60,18 @@ struct BuiltinType {
|
||||
}
|
||||
};
|
||||
|
||||
// Constraint that the function has no return.
|
||||
struct NoReturn {
|
||||
static auto Check(const File& sem_ir, ValidateState& /*state*/,
|
||||
TypeId type_id) -> bool {
|
||||
auto tuple = sem_ir.types().TryGetAs<SemIR::TupleType>(type_id);
|
||||
if (!tuple) {
|
||||
return false;
|
||||
}
|
||||
return sem_ir.type_blocks().Get(tuple->elements_id).empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Constraint that a type is `bool`.
|
||||
using Bool = BuiltinType<InstId::BuiltinBoolType>;
|
||||
|
||||
@@ -157,6 +169,10 @@ using FloatT = TypeParam<0, AnyFloat>;
|
||||
// Not a builtin function.
|
||||
constexpr BuiltinInfo None = {"", nullptr};
|
||||
|
||||
// Prints an argument.
|
||||
constexpr BuiltinInfo PrintInt = {"print.int",
|
||||
ValidateSignature<auto(AnyInt)->NoReturn>};
|
||||
|
||||
// Returns the `i32` type. Doesn't take a bit size because we need an integer
|
||||
// type as a basis for that.
|
||||
constexpr BuiltinInfo IntMakeType32 = {"int.make_type_32",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#endif
|
||||
|
||||
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(None)
|
||||
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(PrintInt)
|
||||
|
||||
// Type factories.
|
||||
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeType32)
|
||||
|
||||
Reference in New Issue
Block a user