diff options
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/CTypeGen.cc | 17 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 7 | ||||
| -rw-r--r-- | lib/CodeGen/InputGen.cc | 328 | ||||
| -rw-r--r-- | lib/CodeGen/NativeTypeSystem.cc | 34 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 30 |
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()); } |
