aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/CTypeGen.cc17
-rw-r--r--lib/CodeGen/CodeGen.cc7
-rw-r--r--lib/CodeGen/InputGen.cc328
-rw-r--r--lib/CodeGen/NativeTypeSystem.cc34
-rw-r--r--lib/CodeGen/StatementGen.cc30
5 files changed, 351 insertions, 65 deletions
diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc
index 18326cb..90f0aa3 100644
--- a/lib/CodeGen/CTypeGen.cc
+++ b/lib/CodeGen/CTypeGen.cc
@@ -186,23 +186,10 @@ void CodeGen::emitCOutput(Value *Val, const TypeInfo *Info, bool AppendNewline)
Builder->CreateCall(PrintfFunc, {Fmt, Val});
}
-void CodeGen::emitCInput(Value *Addr, const TypeInfo *Info) {
+void CodeGen::emitCInput(Value *Addr, const TypeInfo *Info, int Line) {
if (Info->Kind != TypeKind::CScalar || Info->isVoid()) {
reportError("INPUT is not supported for C pointer or void type %s", Info->Name.c_str());
return;
}
- if (Info->isBoolean()) {
- const TypeInfo *NativeInt = resolveType("C_INT");
- AllocaInst *Temp = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(),
- NativeInt->LLVMType, "c_bool_input");
- Builder->CreateStore(Types->getZeroValue("C_INT"), Temp);
- Value *Fmt = Builder->CreateGlobalString("%d", "fmt_c_input_bool", 0, TheModule.get());
- Builder->CreateCall(ScanfFunc, {Fmt, Temp});
- Value *Input = Builder->CreateLoad(NativeInt->LLVMType, Temp);
- Value *Bool = convertNumericValue(Input, NativeInt, Info);
- Builder->CreateStore(Bool, Addr);
- return;
- }
- Value *Fmt = Builder->CreateGlobalString(Info->CInputFormat, "fmt_c_input", 0, TheModule.get());
- Builder->CreateCall(ScanfFunc, {Fmt, Addr});
+ emitScalarInput(Addr, Info, Line);
}
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 772dc82..64ed7c4 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -94,17 +94,10 @@ void CodeGen::SetupExternalFunctions() {
FunctionType *PrintfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true);
PrintfFunc = TheModule->getOrInsertFunction("printf", PrintfType);
- FunctionType *ScanfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true);
- ScanfFunc = TheModule->getOrInsertFunction("scanf", ScanfType);
-
PrintfFormatStr = Builder->CreateGlobalString("%lld\n", "fmt_nl", 0, TheModule.get());
- PrintfFloatFormatStr = Builder->CreateGlobalString("%f\n", "fmt_flt", 0, TheModule.get());
PrintfStringFormatStr = Builder->CreateGlobalString("%s\n", "fmt_str", 0, TheModule.get());
PrintfCharFormatStr = Builder->CreateGlobalString("%c\n", "fmt_chr", 0, TheModule.get());
- ScanfFormatStr = Builder->CreateGlobalString("%lld", "fmt_in", 0, TheModule.get());
- ScanfFloatFormatStr = Builder->CreateGlobalString("%lf", "fmt_in_flt", 0, TheModule.get());
-
TrueStr = Builder->CreateGlobalString("TRUE", "str_true", 0, TheModule.get());
FalseStr = Builder->CreateGlobalString("FALSE", "str_false", 0, TheModule.get());
EmptyStringStr = Builder->CreateGlobalString("", "str_empty", 0, TheModule.get());
diff --git a/lib/CodeGen/InputGen.cc b/lib/CodeGen/InputGen.cc
new file mode 100644
index 0000000..349e7ce
--- /dev/null
+++ b/lib/CodeGen/InputGen.cc
@@ -0,0 +1,328 @@
+#include "cps/CodeGen.h"
+#include "cps/RuntimeCheck.h"
+#include "llvm/IR/Intrinsics.h"
+
+using namespace llvm;
+using namespace cps;
+
+namespace {
+// Defines (once per module) `ptr __cps_input_token()`: reads one whitespace-delimited token from
+// stdin into a malloc'd NUL-terminated buffer, or returns null at end of input. After the token it
+// skips spaces, tabs and carriage returns and consumes one newline if it follows, so the next
+// INPUT of a STRING starts on the following line. The caller frees the buffer.
+FunctionCallee getInputTokenHelper(Module &M) {
+ LLVMContext &Ctx = M.getContext();
+ Type *I8 = Type::getInt8Ty(Ctx), *I32 = Type::getInt32Ty(Ctx), *I64 = Type::getInt64Ty(Ctx);
+ PointerType *Ptr = PointerType::getUnqual(Ctx);
+ FunctionType *HelperTy = FunctionType::get(Ptr, {}, false);
+ if (Function *Existing = M.getFunction("__cps_input_token")) return {HelperTy, Existing};
+
+ FunctionCallee Getchar = M.getOrInsertFunction("getchar", FunctionType::get(I32, {}, false));
+ FunctionCallee Ungetc = M.getOrInsertFunction("ungetc", FunctionType::get(I32, {I32, Ptr}, false));
+ FunctionCallee Isspace = M.getOrInsertFunction("isspace", FunctionType::get(I32, {I32}, false));
+ FunctionCallee Realloc = M.getOrInsertFunction("realloc", FunctionType::get(Ptr, {Ptr, I64}, false));
+
+ Function *F = Function::Create(HelperTy, Function::InternalLinkage, "__cps_input_token", &M);
+ BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
+ BasicBlock *SkipHead = BasicBlock::Create(Ctx, "skip.head", F);
+ BasicBlock *SkipTest = BasicBlock::Create(Ctx, "skip.test", F);
+ BasicBlock *Append = BasicBlock::Create(Ctx, "append", F);
+ BasicBlock *Grow = BasicBlock::Create(Ctx, "grow", F);
+ BasicBlock *Store = BasicBlock::Create(Ctx, "store", F);
+ BasicBlock *ReadTest = BasicBlock::Create(Ctx, "read.test", F);
+ BasicBlock *Finish = BasicBlock::Create(Ctx, "finish", F);
+ BasicBlock *TrailHead = BasicBlock::Create(Ctx, "trail.head", F);
+ BasicBlock *TrailSkip = BasicBlock::Create(Ctx, "trail.skip", F);
+ BasicBlock *TrailEnd = BasicBlock::Create(Ctx, "trail.end", F);
+ BasicBlock *PushBack = BasicBlock::Create(Ctx, "pushback", F);
+ BasicBlock *Done = BasicBlock::Create(Ctx, "done", F);
+ BasicBlock *Eof = BasicBlock::Create(Ctx, "eof", F);
+
+ IRBuilder<> B(Entry);
+ Value *Buf = B.CreateAlloca(Ptr, nullptr, "buf");
+ Value *Len = B.CreateAlloca(I64, nullptr, "len");
+ Value *Cap = B.CreateAlloca(I64, nullptr, "cap");
+ Value *Cur = B.CreateAlloca(I32, nullptr, "cur");
+ B.CreateStore(ConstantPointerNull::get(Ptr), Buf);
+ B.CreateStore(ConstantInt::get(I64, 0), Len);
+ B.CreateStore(ConstantInt::get(I64, 0), Cap);
+ B.CreateBr(SkipHead);
+
+ // Leading whitespace.
+ B.SetInsertPoint(SkipHead);
+ Value *First = B.CreateCall(Getchar);
+ B.CreateStore(First, Cur);
+ B.CreateCondBr(B.CreateICmpEQ(First, ConstantInt::get(I32, -1)), Eof, SkipTest);
+ B.SetInsertPoint(SkipTest);
+ Value *IsSpace = B.CreateICmpNE(B.CreateCall(Isspace, {First}), ConstantInt::get(I32, 0));
+ B.CreateCondBr(IsSpace, SkipHead, Append);
+
+ // Append Cur to the buffer, keeping room for the byte and the terminator.
+ B.SetInsertPoint(Append);
+ Value *L = B.CreateLoad(I64, Len), *C = B.CreateLoad(I64, Cap);
+ B.CreateCondBr(B.CreateICmpUGE(B.CreateAdd(L, ConstantInt::get(I64, 2)), C), Grow, Store);
+ B.SetInsertPoint(Grow);
+ Value *NewCap = B.CreateSelect(B.CreateICmpEQ(C, ConstantInt::get(I64, 0)),
+ ConstantInt::get(I64, 32), B.CreateMul(C, ConstantInt::get(I64, 2)));
+ B.CreateStore(B.CreateCall(Realloc, {B.CreateLoad(Ptr, Buf), NewCap}), Buf);
+ B.CreateStore(NewCap, Cap);
+ B.CreateBr(Store);
+ B.SetInsertPoint(Store);
+ Value *StoreLen = B.CreateLoad(I64, Len);
+ Value *Slot = B.CreateGEP(I8, B.CreateLoad(Ptr, Buf), {StoreLen});
+ B.CreateStore(B.CreateTrunc(B.CreateLoad(I32, Cur), I8), Slot);
+ B.CreateStore(B.CreateAdd(StoreLen, ConstantInt::get(I64, 1)), Len);
+ Value *Next = B.CreateCall(Getchar);
+ B.CreateStore(Next, Cur);
+ B.CreateCondBr(B.CreateICmpEQ(Next, ConstantInt::get(I32, -1)), Finish, ReadTest);
+ B.SetInsertPoint(ReadTest);
+ Value *NextSpace = B.CreateICmpNE(B.CreateCall(Isspace, {Next}), ConstantInt::get(I32, 0));
+ B.CreateCondBr(NextSpace, Finish, Append);
+
+ // Terminate, then skip trailing spaces/tabs/CRs and one newline.
+ B.SetInsertPoint(Finish);
+ Value *EndLen = B.CreateLoad(I64, Len);
+ B.CreateStore(ConstantInt::get(I8, 0), B.CreateGEP(I8, B.CreateLoad(Ptr, Buf), {EndLen}));
+ B.CreateBr(TrailHead);
+ B.SetInsertPoint(TrailHead);
+ Value *Ch = B.CreateLoad(I32, Cur);
+ Value *Blank = B.CreateOr(B.CreateOr(B.CreateICmpEQ(Ch, ConstantInt::get(I32, ' ')),
+ B.CreateICmpEQ(Ch, ConstantInt::get(I32, '\t'))),
+ B.CreateICmpEQ(Ch, ConstantInt::get(I32, '\r')));
+ B.CreateCondBr(Blank, TrailSkip, TrailEnd);
+ B.SetInsertPoint(TrailSkip);
+ B.CreateStore(B.CreateCall(Getchar), Cur);
+ B.CreateBr(TrailHead);
+ B.SetInsertPoint(TrailEnd);
+ Value *End = B.CreateLoad(I32, Cur);
+ Value *KeepsNothing = B.CreateOr(B.CreateICmpEQ(End, ConstantInt::get(I32, '\n')),
+ B.CreateICmpEQ(End, ConstantInt::get(I32, -1)));
+ B.CreateCondBr(KeepsNothing, Done, PushBack);
+ B.SetInsertPoint(PushBack);
+ // The stdin FILE* is a platform-specific symbol; host and target match.
+#if defined(_WIN32)
+ FunctionCallee IobFunc = M.getOrInsertFunction("__acrt_iob_func", FunctionType::get(Ptr, {I32}, false));
+ Value *Stdin = B.CreateCall(IobFunc, {ConstantInt::get(I32, 0)});
+#else
+#if defined(__APPLE__)
+ const char *StdinName = "__stdinp";
+#else
+ const char *StdinName = "stdin";
+#endif
+ GlobalVariable *StdinVar = M.getGlobalVariable(StdinName);
+ if (!StdinVar)
+ StdinVar = new GlobalVariable(M, Ptr, false, GlobalValue::ExternalLinkage, nullptr, StdinName);
+ Value *Stdin = B.CreateLoad(Ptr, StdinVar);
+#endif
+ B.CreateCall(Ungetc, {End, Stdin});
+ B.CreateBr(Done);
+ B.SetInsertPoint(Done);
+ B.CreateRet(B.CreateLoad(Ptr, Buf));
+ B.SetInsertPoint(Eof);
+ B.CreateRet(ConstantPointerNull::get(Ptr));
+ return {HelperTy, F};
+}
+
+// Defines `i1 __cps_parse_int(ptr token, ptr out)`: an optionally signed decimal integer that fills
+// the whole token and fits in INTEGER (what strtoll accepts in base 10, without errno).
+FunctionCallee getParseIntHelper(Module &M) {
+ LLVMContext &Ctx = M.getContext();
+ Type *I1 = Type::getInt1Ty(Ctx), *I8 = Type::getInt8Ty(Ctx), *I64 = Type::getInt64Ty(Ctx);
+ PointerType *Ptr = PointerType::getUnqual(Ctx);
+ FunctionType *HelperTy = FunctionType::get(I1, {Ptr, Ptr}, false);
+ if (Function *Existing = M.getFunction("__cps_parse_int")) return {HelperTy, Existing};
+
+ Function *F = Function::Create(HelperTy, Function::InternalLinkage, "__cps_parse_int", &M);
+ Value *Token = F->getArg(0), *Out = F->getArg(1);
+ BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
+ BasicBlock *Head = BasicBlock::Create(Ctx, "head", F), *Body = BasicBlock::Create(Ctx, "body", F);
+ BasicBlock *Tail = BasicBlock::Create(Ctx, "tail", F), *Negate = BasicBlock::Create(Ctx, "negate", F);
+ BasicBlock *Finish = BasicBlock::Create(Ctx, "finish", F), *Fail = BasicBlock::Create(Ctx, "fail", F);
+
+ IRBuilder<> B(Entry);
+ Value *Acc = B.CreateAlloca(I64, nullptr, "acc");
+ Value *Pos = B.CreateAlloca(Ptr, nullptr, "pos");
+ Value *Sign = B.CreateLoad(I8, Token);
+ Value *IsNeg = B.CreateICmpEQ(Sign, ConstantInt::get(I8, '-'));
+ Value *HasSign = B.CreateOr(IsNeg, B.CreateICmpEQ(Sign, ConstantInt::get(I8, '+')));
+ Value *Start = B.CreateGEP(I8, Token, {B.CreateZExt(HasSign, I64)});
+ B.CreateStore(ConstantInt::get(I64, 0), Acc);
+ B.CreateStore(Start, Pos);
+ B.CreateBr(Head);
+
+ // Accumulate negatively so INTEGER's minimum is representable.
+ B.SetInsertPoint(Head);
+ Value *P = B.CreateLoad(Ptr, Pos);
+ Value *Digit = B.CreateSub(B.CreateLoad(I8, P), ConstantInt::get(I8, '0'));
+ B.CreateCondBr(B.CreateICmpULT(Digit, ConstantInt::get(I8, 10)), Body, Tail);
+ B.SetInsertPoint(Body);
+ Value *Old = B.CreateLoad(I64, Acc);
+ Value *Mul = B.CreateBinaryIntrinsic(Intrinsic::smul_with_overflow, Old, ConstantInt::get(I64, 10));
+ Value *Sub = B.CreateBinaryIntrinsic(Intrinsic::ssub_with_overflow, B.CreateExtractValue(Mul, 0),
+ B.CreateZExt(Digit, I64));
+ Value *Overflow = B.CreateOr(B.CreateExtractValue(Mul, 1), B.CreateExtractValue(Sub, 1));
+ B.CreateStore(B.CreateExtractValue(Sub, 0), Acc);
+ B.CreateStore(B.CreateGEP(I8, P, {ConstantInt::get(I64, 1)}), Pos);
+ BasicBlock *Next = BasicBlock::Create(Ctx, "next", F);
+ B.CreateCondBr(Overflow, Fail, Next);
+ B.SetInsertPoint(Next);
+ B.CreateBr(Head);
+
+ B.SetInsertPoint(Tail);
+ Value *AtEnd = B.CreateICmpEQ(B.CreateLoad(I8, P), ConstantInt::get(I8, 0));
+ Value *HasDigits = B.CreateICmpNE(P, Start);
+ BasicBlock *Sign2 = BasicBlock::Create(Ctx, "sign", F);
+ B.CreateCondBr(B.CreateAnd(AtEnd, HasDigits), Sign2, Fail);
+ B.SetInsertPoint(Sign2);
+ Value *Magnitude = B.CreateLoad(I64, Acc);
+ B.CreateCondBr(IsNeg, Finish, Negate);
+ B.SetInsertPoint(Negate);
+ // +x needs -acc, which overflows only for INTEGER's minimum.
+ BasicBlock *Positive = BasicBlock::Create(Ctx, "positive", F);
+ B.CreateCondBr(B.CreateICmpEQ(Magnitude, ConstantInt::get(I64, APInt::getSignedMinValue(64))), Fail, Positive);
+ B.SetInsertPoint(Positive);
+ B.CreateStore(B.CreateNeg(Magnitude), Out);
+ B.CreateRet(ConstantInt::getTrue(Ctx));
+ B.SetInsertPoint(Finish);
+ B.CreateStore(Magnitude, Out);
+ B.CreateRet(ConstantInt::getTrue(Ctx));
+ B.SetInsertPoint(Fail);
+ B.CreateRet(ConstantInt::getFalse(Ctx));
+ return {HelperTy, F};
+}
+
+// Defines `i1 __cps_parse_real(ptr token, ptr out)`: the rule of parseNumber in pseudocode-lib/
+// string.cc (shared by IS_NUM and STR_TO_NUM): strtod must consume the whole token and must not
+// overflow to infinity. A finite overflow is told from an "inf" literal by the missing letter i.
+FunctionCallee getParseRealHelper(Module &M) {
+ LLVMContext &Ctx = M.getContext();
+ Type *I1 = Type::getInt1Ty(Ctx), *I8 = Type::getInt8Ty(Ctx), *I32 = Type::getInt32Ty(Ctx);
+ Type *DoubleTy = Type::getDoubleTy(Ctx);
+ PointerType *Ptr = PointerType::getUnqual(Ctx);
+ FunctionType *HelperTy = FunctionType::get(I1, {Ptr, Ptr}, false);
+ if (Function *Existing = M.getFunction("__cps_parse_real")) return {HelperTy, Existing};
+
+ FunctionCallee Strtod = M.getOrInsertFunction("strtod", FunctionType::get(DoubleTy, {Ptr, Ptr}, false));
+ FunctionCallee Strchr = M.getOrInsertFunction("strchr", FunctionType::get(Ptr, {Ptr, I32}, false));
+ Function *F = Function::Create(HelperTy, Function::InternalLinkage, "__cps_parse_real", &M);
+ Value *Token = F->getArg(0), *Out = F->getArg(1);
+ IRBuilder<> B(BasicBlock::Create(Ctx, "entry", F));
+ Value *EndSlot = B.CreateAlloca(Ptr, nullptr, "end");
+ Value *Number = B.CreateCall(Strtod, {Token, EndSlot});
+ Value *Consumed = B.CreateICmpEQ(B.CreateLoad(I8, B.CreateLoad(Ptr, EndSlot)), ConstantInt::get(I8, 0));
+ Value *Progress = B.CreateICmpNE(B.CreateLoad(Ptr, EndSlot), Token);
+ Value *IsInf = B.CreateFCmpOEQ(B.CreateUnaryIntrinsic(Intrinsic::fabs, Number),
+ ConstantFP::getInfinity(DoubleTy));
+ Value *NoLowerI = B.CreateIsNull(B.CreateCall(Strchr, {Token, ConstantInt::get(I32, 'i')}));
+ Value *NoUpperI = B.CreateIsNull(B.CreateCall(Strchr, {Token, ConstantInt::get(I32, 'I')}));
+ Value *Overflowed = B.CreateAnd(IsInf, B.CreateAnd(NoLowerI, NoUpperI));
+ B.CreateStore(Number, Out);
+ B.CreateRet(B.CreateAnd(B.CreateAnd(Consumed, Progress), B.CreateNot(Overflowed)));
+ return {HelperTy, F};
+}
+
+// Defines `i32 __cps_parse_bool(ptr token)`: 1 for TRUE, 0 for FALSE in any case, -1 otherwise.
+FunctionCallee getParseBoolHelper(Module &M) {
+ LLVMContext &Ctx = M.getContext();
+ Type *I8 = Type::getInt8Ty(Ctx), *I32 = Type::getInt32Ty(Ctx), *I64 = Type::getInt64Ty(Ctx);
+ PointerType *Ptr = PointerType::getUnqual(Ctx);
+ FunctionType *HelperTy = FunctionType::get(I32, {Ptr}, false);
+ if (Function *Existing = M.getFunction("__cps_parse_bool")) return {HelperTy, Existing};
+
+ FunctionCallee Strlen = M.getOrInsertFunction("strlen", FunctionType::get(I64, {Ptr}, false));
+ Function *F = Function::Create(HelperTy, Function::InternalLinkage, "__cps_parse_bool", &M);
+ Value *Token = F->getArg(0);
+ BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
+ BasicBlock *IsTrue = BasicBlock::Create(Ctx, "true", F), *IsFalse = BasicBlock::Create(Ctx, "false", F);
+ BasicBlock *Other = BasicBlock::Create(Ctx, "other", F), *Not4 = BasicBlock::Create(Ctx, "not4", F);
+ IRBuilder<> B(Entry);
+ Value *Length = B.CreateCall(Strlen, {Token});
+ // ORing 0x20 folds ASCII upper case to lower case; no other byte folds onto a letter.
+ auto Matches = [&](const char *Word) {
+ Value *All = ConstantInt::getTrue(Ctx);
+ for (unsigned I = 0; Word[I]; ++I) {
+ Value *Byte = B.CreateOr(B.CreateLoad(I8, B.CreateGEP(I8, Token, {ConstantInt::get(I64, I)})),
+ ConstantInt::get(I8, 0x20));
+ All = B.CreateAnd(All, B.CreateICmpEQ(Byte, ConstantInt::get(I8, Word[I])));
+ }
+ return All;
+ };
+ BasicBlock *Check4 = BasicBlock::Create(Ctx, "check4", F), *Check5 = BasicBlock::Create(Ctx, "check5", F);
+ B.CreateCondBr(B.CreateICmpEQ(Length, ConstantInt::get(I64, 4)), Check4, Not4);
+ B.SetInsertPoint(Check4);
+ B.CreateCondBr(Matches("true"), IsTrue, Other);
+ B.SetInsertPoint(Not4);
+ B.CreateCondBr(B.CreateICmpEQ(Length, ConstantInt::get(I64, 5)), Check5, Other);
+ B.SetInsertPoint(Check5);
+ B.CreateCondBr(Matches("false"), IsFalse, Other);
+ B.SetInsertPoint(IsTrue);
+ B.CreateRet(ConstantInt::get(I32, 1));
+ B.SetInsertPoint(IsFalse);
+ B.CreateRet(ConstantInt::get(I32, 0));
+ B.SetInsertPoint(Other);
+ B.CreateRet(ConstantInt::get(I32, -1));
+ return {HelperTy, F};
+}
+} // namespace
+
+// Reads one token from stdin and stores it, validated, at Addr. A malformed token or the end of
+// input is a runtime error. C scalars follow the same token model; integers must fit the C type.
+void CodeGen::emitScalarInput(Value *Addr, const TypeInfo *Info, int Line) {
+ Module &M = *TheModule;
+ Type *I32 = Type::getInt32Ty(*TheContext), *I64 = Type::getInt64Ty(*TheContext);
+ Type *DoubleTy = Type::getDoubleTy(*TheContext);
+ PointerType *Ptr = PointerType::getUnqual(*TheContext);
+ FunctionCallee Free = M.getOrInsertFunction("free", FunctionType::get(Type::getVoidTy(*TheContext), {Ptr}, false));
+ FunctionCallee Strlen = M.getOrInsertFunction("strlen", FunctionType::get(I64, {Ptr}, false));
+
+ Value *Token = Builder->CreateCall(getInputTokenHelper(M), {}, "input_token");
+ RuntimeChecker->emitCheck(Builder->CreateIsNull(Token),
+ Builder->CreateGlobalString("[Fatal] line %d: INPUT reached the end of input\n", "err_input_eof", 0, &M),
+ {RuntimeChecker->lineValue(Line)});
+ Value *TypeName = Builder->CreateGlobalString(Info->Name, "input_type_name", 0, &M);
+ Value *InvalidMsg = Builder->CreateGlobalString("[Fatal] line %d: Invalid %s input '%s'\n",
+ "err_input_invalid", 0, &M);
+ auto Require = [&](Value *Valid) {
+ RuntimeChecker->emitCheck(Builder->CreateNot(Valid), InvalidMsg,
+ {RuntimeChecker->lineValue(Line), TypeName, Token});
+ };
+ Function *Fn = Builder->GetInsertBlock()->getParent();
+
+ if (Info->isChar()) {
+ Require(Builder->CreateICmpEQ(Builder->CreateCall(Strlen, {Token}), ConstantInt::get(I64, 1)));
+ Builder->CreateStore(Builder->CreateLoad(Info->LLVMType, Token), Addr);
+ } else if (Info->isBoolean() && !Info->isC()) {
+ Value *Parsed = Builder->CreateCall(getParseBoolHelper(M), {Token});
+ Require(Builder->CreateICmpSGE(Parsed, ConstantInt::get(I32, 0)));
+ Builder->CreateStore(Builder->CreateICmpNE(Parsed, ConstantInt::get(I32, 0)), Addr);
+ } else if (Info->isReal()) {
+ AllocaInst *Slot = CreateEntryBlockAlloca(Fn, DoubleTy, "input_real");
+ Require(Builder->CreateCall(getParseRealHelper(M), {Token, Slot}));
+ Value *Number = Builder->CreateLoad(DoubleTy, Slot);
+ Type *Target = Info->LLVMType;
+ if (Target->isFloatTy()) Number = Builder->CreateFPTrunc(Number, Target);
+ else if (Target != DoubleTy) Number = Builder->CreateFPExt(Number, Target);
+ Builder->CreateStore(Number, Addr);
+ } else {
+ AllocaInst *Slot = CreateEntryBlockAlloca(Fn, I64, "input_int");
+ Require(Builder->CreateCall(getParseIntHelper(M), {Token, Slot}));
+ Value *Number = Builder->CreateLoad(I64, Slot);
+ if (Info->isBoolean()) {
+ Builder->CreateStore(Builder->CreateICmpNE(Number, ConstantInt::get(I64, 0)), Addr);
+ } else {
+ unsigned Bits = Info->LLVMType->getIntegerBitWidth();
+ Value *Stored = Number;
+ if (Bits < 64) {
+ Type *Narrow = Info->LLVMType;
+ Stored = Builder->CreateTrunc(Number, Narrow);
+ Value *Back = Info->isC() && !Info->CSigned ? Builder->CreateZExt(Stored, I64)
+ : Builder->CreateSExt(Stored, I64);
+ Require(Builder->CreateICmpEQ(Back, Number));
+ }
+ if (Info->isC() && !Info->CSigned)
+ Require(Builder->CreateICmpSGE(Number, ConstantInt::get(I64, 0)));
+ Builder->CreateStore(Stored, Addr);
+ }
+ }
+ Builder->CreateCall(Free, {Token});
+}
diff --git a/lib/CodeGen/NativeTypeSystem.cc b/lib/CodeGen/NativeTypeSystem.cc
index d5d1c93..c1be163 100644
--- a/lib/CodeGen/NativeTypeSystem.cc
+++ b/lib/CodeGen/NativeTypeSystem.cc
@@ -45,7 +45,7 @@ void TypeSystem::registerBuiltins() {
// of assumed LP64/LLP64 widths. LLVM's addressable bytes are eight bits.
static_assert(CHAR_BIT == 8, "LLVM requires eight-bit addressable bytes");
auto Integral = [&](auto Sample, const char *Name, unsigned Rank,
- const char *InputFormat, const char *OutputFormat) {
+ const char *OutputFormat) {
using T = decltype(Sample);
TypeInfo Info;
Info.Name = cTypeName(Name);
@@ -54,36 +54,34 @@ void TypeSystem::registerBuiltins() {
Info.LLVMType = Type::getIntNTy(Context, sizeof(T) * CHAR_BIT);
Info.CRank = Rank;
Info.CSigned = std::is_signed<T>::value;
- Info.CInputFormat = InputFormat;
Info.COutputFormat = OutputFormat;
Types.emplace(Info.Name, std::move(Info));
};
- Integral(char{}, "char", 2, " %c", "%c");
- Integral(static_cast<signed char>(0), "signed char", 2, "%hhd", "%hhd");
- Integral(static_cast<unsigned char>(0), "unsigned char", 2, "%hhu", "%hhu");
- Integral(short{}, "short", 3, "%hd", "%hd");
- Integral(static_cast<unsigned short>(0), "unsigned short", 3, "%hu", "%hu");
- Integral(int{}, "int", 4, "%d", "%d");
- Integral(static_cast<unsigned int>(0), "unsigned int", 4, "%u", "%u");
- Integral(long{}, "long", 5, "%ld", "%ld");
- Integral(static_cast<unsigned long>(0), "unsigned long", 5, "%lu", "%lu");
- Integral(static_cast<long long>(0), "long long", 6, "%lld", "%lld");
- Integral(static_cast<unsigned long long>(0), "unsigned long long", 6, "%llu", "%llu");
+ Integral(char{}, "char", 2, "%c");
+ Integral(static_cast<signed char>(0), "signed char", 2, "%hhd");
+ Integral(static_cast<unsigned char>(0), "unsigned char", 2, "%hhu");
+ Integral(short{}, "short", 3, "%hd");
+ Integral(static_cast<unsigned short>(0), "unsigned short", 3, "%hu");
+ Integral(int{}, "int", 4, "%d");
+ Integral(static_cast<unsigned int>(0), "unsigned int", 4, "%u");
+ Integral(long{}, "long", 5, "%ld");
+ Integral(static_cast<unsigned long>(0), "unsigned long", 5, "%lu");
+ Integral(static_cast<long long>(0), "long long", 6, "%lld");
+ Integral(static_cast<unsigned long long>(0), "unsigned long long", 6, "%llu");
- auto Floating = [&](auto Sample, const char *Name, unsigned Rank, const char *Format) {
+ auto Floating = [&](auto Sample, const char *Name, unsigned Rank) {
TypeInfo Info;
Info.Name = cTypeName(Name);
Info.CIdentity = Name;
Info.Kind = TypeKind::CScalar;
Info.LLVMType = nativeFloatType<decltype(Sample)>(Context);
Info.CRank = Rank;
- Info.CInputFormat = Format;
Info.COutputFormat = Rank == 9 ? "%Lf" : "%f";
Types.emplace(Info.Name, std::move(Info));
};
- Floating(float{}, "float", 7, "%f");
- Floating(double{}, "double", 8, "%lf");
- Floating(static_cast<long double>(0), "long double", 9, "%Lf");
+ Floating(float{}, "float", 7);
+ Floating(double{}, "double", 8);
+ Floating(static_cast<long double>(0), "long double", 9);
TypeInfo Bool;
Bool.Name = cTypeName("_Bool");
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index 0b14cf2..c161049 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -252,35 +252,15 @@ void CodeGen::emitInputStmt(InputStmtAST *In) {
return;
}
- std::vector<Value*> Args;
if (TypeInfo->isC()) {
- emitCInput(Target.Addr, TypeInfo);
- } else if (TypeInfo->isReal()) {
- Args.push_back(ScanfFloatFormatStr);
- Args.push_back(Target.Addr);
- Builder->CreateCall(ScanfFunc, Args);
- } else if (TypeInfo->isBoolean()) {
- AllocaInst *TempInt = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), "tmp_bool_input");
- Args.push_back(ScanfFormatStr);
- Args.push_back(TempInt);
- Builder->CreateCall(ScanfFunc, Args);
-
- Value *Val = Builder->CreateLoad(Type::getInt64Ty(*TheContext), TempInt);
- Value *BoolVal = Builder->CreateICmpNE(Val, ConstantInt::get(*TheContext, APInt(64, 0)), "bool_cast");
- Builder->CreateStore(BoolVal, Target.Addr);
+ emitCInput(Target.Addr, TypeInfo, In->getLine());
} else if (TypeInfo->isString()) {
emitMethodCall(TypeInfo->Name, Target.Addr, "READ_INPUT", {}, In->getLine());
- } else if (TypeInfo->isChar()) {
- Value *CharFmt = Builder->CreateGlobalString(" %c", "fmt_in_char", 0, TheModule.get());
- Args.push_back(CharFmt);
- Args.push_back(Target.Addr);
- Builder->CreateCall(ScanfFunc, Args);
- } else if (TypeInfo->Kind == TypeKind::Integer) {
- Args.push_back(ScanfFormatStr);
- Args.push_back(Target.Addr);
- Builder->CreateCall(ScanfFunc, Args);
+ } else if (TypeInfo->Kind == TypeKind::Integer || TypeInfo->isReal() || TypeInfo->isBoolean() ||
+ TypeInfo->isChar()) {
+ emitScalarInput(Target.Addr, TypeInfo, In->getLine());
} else {
- // Enum/Record/Pointer and any future kind: scanf'ing raw bytes
+ // Enum/Record/Pointer and any future kind: reading raw bytes
// into typed storage would corrupt it — reject explicitly.
reportError("INPUT is not supported for a value of type %s", TypeInfo->Name.c_str());
}