Skip to content

Commit c3f1774

Browse files
committed
Initial buffer framing code
Address review comments Integer output data editing (I,B,O,Z) Full integer output formatting Stub out some work in progress Progress on E output data editing E, D, EN, and ES output editing done Fw.d output editing Real G output editing G output editing for reals Make environment a distinct module CHARACTER and LOGICAL output editing Minimal decimal representations for E0, F0, G0 editing Move real output editing code into its own file Fix/dodge some GCC build problems Prep work for external I/O statement state External HELLO, WORLD Fix build problem with GCC Add virtual destructors where needed Add new test
1 parent bedca14 commit c3f1774

27 files changed

+1797
-204
lines changed

lib/decimal/binary-to-decimal.cpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ template<int PREC, int LOG10RADIX>
108108
ConversionToDecimalResult
109109
BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToDecimal(char *buffer,
110110
std::size_t n, enum DecimalConversionFlags flags, int maxDigits) const {
111-
if (n < static_cast<std::size_t>(3 + digits_ * LOG10RADIX) || maxDigits < 1) {
111+
if (n < static_cast<std::size_t>(3 + digits_ * LOG10RADIX)) {
112112
return {nullptr, 0, 0, Overflow};
113113
}
114114
char *start{buffer};
@@ -160,18 +160,21 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToDecimal(char *buffer,
160160
while (p[-1] == '0') {
161161
--p;
162162
}
163-
if (p <= start + maxDigits) {
163+
char *end{start + maxDigits};
164+
if (maxDigits == 0) {
165+
p = end;
166+
}
167+
if (p <= end) {
164168
*p = '\0';
165169
return {buffer, static_cast<std::size_t>(p - buffer), expo, Exact};
166170
} else {
167171
// Apply a digit limit, possibly with rounding.
168-
char *end{start + maxDigits};
169172
bool incr{false};
170173
switch (rounding_) {
171174
case RoundNearest:
172175
case RoundDefault:
173-
incr =
174-
*end > '5' || (*end == '5' && (p > end || ((p[-1] - '0') & 1) != 0));
176+
incr = *end > '5' ||
177+
(*end == '5' && (p > end + 1 || ((end[-1] - '0') & 1) != 0));
175178
break;
176179
case RoundUp: incr = !isNegative_; break;
177180
case RoundDown: incr = isNegative_; break;
@@ -190,6 +193,7 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToDecimal(char *buffer,
190193
++p[-1];
191194
}
192195
}
196+
193197
*p = '\0';
194198
return {buffer, static_cast<std::size_t>(p - buffer), expo, Inexact};
195199
}

runtime/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
add_library(FortranRuntime
1010
ISO_Fortran_binding.cpp
11+
buffer.cpp
1112
derived-type.cpp
1213
descriptor.cpp
14+
environment.cpp
1315
file.cpp
1416
format.cpp
1517
io-api.cpp
@@ -22,6 +24,7 @@ add_library(FortranRuntime
2224
tools.cpp
2325
transformational.cpp
2426
type-code.cpp
27+
unit.cpp
2528
)
2629

2730
target_link_libraries(FortranRuntime

runtime/buffer.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//===-- runtime/buffer.cpp --------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "buffer.h"
10+
#include <algorithm>
11+
12+
namespace Fortran::runtime::io {
13+
14+
// Here's a very old trick for shifting circular buffer data cheaply
15+
// without a need for a temporary array.
16+
void LeftShiftBufferCircularly(
17+
char *buffer, std::size_t bytes, std::size_t shift) {
18+
// Assume that we start with "efgabcd" and the left shift is 3.
19+
std::reverse(buffer, buffer + shift); // "gfeabcd"
20+
std::reverse(buffer, buffer + bytes); // "dcbaefg"
21+
std::reverse(buffer, buffer + bytes - shift); // "abcdefg"
22+
}
23+
}

runtime/buffer.h

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
//===-- runtime/buffer.h ----------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// External file buffering
10+
11+
#ifndef FORTRAN_RUNTIME_BUFFER_H_
12+
#define FORTRAN_RUNTIME_BUFFER_H_
13+
14+
#include "io-error.h"
15+
#include "memory.h"
16+
#include <algorithm>
17+
#include <cinttypes>
18+
#include <cstring>
19+
20+
namespace Fortran::runtime::io {
21+
22+
void LeftShiftBufferCircularly(char *, std::size_t bytes, std::size_t shift);
23+
24+
// Maintains a view of a contiguous region of a file in a memory buffer.
25+
// The valid data in the buffer may be circular, but any active frame
26+
// will also be contiguous in memory. The requirement stems from the need to
27+
// preserve read data that may be reused by means of Tn/TLn edit descriptors
28+
// without needing to position the file (which may not always be possible,
29+
// e.g. a socket) and a general desire to reduce system call counts.
30+
template<typename STORE> class FileFrame {
31+
public:
32+
using FileOffset = std::int64_t;
33+
34+
~FileFrame() { FreeMemoryAndNullify(buffer_); }
35+
36+
// The valid data in the buffer begins at buffer_[start_] and proceeds
37+
// with possible wrap-around for length_ bytes. The current frame
38+
// is offset by frame_ bytes into that region and is guaranteed to
39+
// be contiguous for at least as many bytes as were requested.
40+
41+
FileOffset FrameAt() const { return fileOffset_ + frame_; }
42+
char *Frame() const { return buffer_ + start_ + frame_; }
43+
std::size_t FrameLength() const {
44+
return std::min(
45+
static_cast<std::size_t>(length_ - frame_), size_ - (start_ + frame_));
46+
}
47+
48+
// Returns a short frame at a non-fatal EOF. Can return a long frame as well.
49+
std::size_t ReadFrame(
50+
FileOffset at, std::size_t bytes, IoErrorHandler &handler) {
51+
Flush(handler);
52+
Reallocate(bytes, handler);
53+
if (at < fileOffset_ || at > fileOffset_ + length_) {
54+
Reset(at);
55+
}
56+
frame_ = static_cast<std::size_t>(at - fileOffset_);
57+
if (start_ + frame_ + bytes > size_) {
58+
DiscardLeadingBytes(frame_, handler);
59+
if (start_ + bytes > size_) {
60+
// Frame would wrap around; shift current data (if any) to force
61+
// contiguity.
62+
RUNTIME_CHECK(handler, length_ < size_);
63+
if (start_ + length_ <= size_) {
64+
// [......abcde..] -> [abcde........]
65+
std::memmove(buffer_, buffer_ + start_, length_);
66+
} else {
67+
// [cde........ab] -> [abcde........]
68+
auto n{start_ + length_ - size_}; // 3 for cde
69+
auto gap{size_ - length_}; // 13 - 5 = 8
70+
RUNTIME_CHECK(handler, length_ >= n);
71+
std::memmove(buffer_ + n, buffer_ + start_, length_ - n); // cdeab
72+
LeftShiftBufferCircularly(buffer_, length_, n); // abcde
73+
}
74+
start_ = 0;
75+
}
76+
}
77+
while (FrameLength() < bytes) {
78+
auto next{start_ + length_};
79+
RUNTIME_CHECK(handler, next < size_);
80+
auto minBytes{bytes - FrameLength()};
81+
auto maxBytes{size_ - next};
82+
auto got{Store().Read(
83+
fileOffset_ + length_, buffer_ + next, minBytes, maxBytes, handler)};
84+
length_ += got;
85+
RUNTIME_CHECK(handler, length_ < size_);
86+
if (got < minBytes) {
87+
break; // error or EOF & program can handle it
88+
}
89+
}
90+
return FrameLength();
91+
}
92+
93+
void WriteFrame(FileOffset at, std::size_t bytes, IoErrorHandler &handler) {
94+
if (!dirty_ || at < fileOffset_ || at > fileOffset_ + length_ ||
95+
start_ + (at - fileOffset_) + bytes > size_) {
96+
Flush(handler);
97+
fileOffset_ = at;
98+
Reallocate(bytes, handler);
99+
}
100+
dirty_ = true;
101+
frame_ = at - fileOffset_;
102+
length_ = std::max(length_, static_cast<std::int64_t>(frame_ + bytes));
103+
}
104+
105+
void Flush(IoErrorHandler &handler) {
106+
if (dirty_) {
107+
while (length_ > 0) {
108+
std::size_t chunk{std::min(static_cast<std::size_t>(length_),
109+
static_cast<std::size_t>(size_ - start_))};
110+
std::size_t put{
111+
Store().Write(fileOffset_, buffer_ + start_, chunk, handler)};
112+
length_ -= put;
113+
start_ += put;
114+
fileOffset_ += put;
115+
if (put < chunk) {
116+
break;
117+
}
118+
}
119+
Reset(fileOffset_);
120+
}
121+
}
122+
123+
private:
124+
STORE &Store() { return static_cast<STORE &>(*this); }
125+
126+
void Reallocate(std::size_t bytes, Terminator &terminator) {
127+
if (bytes > size_) {
128+
char *old{buffer_};
129+
auto oldSize{size_};
130+
size_ = std::max(bytes, minBuffer);
131+
buffer_ =
132+
reinterpret_cast<char *>(AllocateMemoryOrCrash(terminator, size_));
133+
auto chunk{
134+
std::min(length_, static_cast<std::int64_t>(oldSize - start_))};
135+
std::memcpy(buffer_, old + start_, chunk);
136+
start_ = 0;
137+
std::memcpy(buffer_ + chunk, old, length_ - chunk);
138+
FreeMemory(old);
139+
}
140+
}
141+
142+
void Reset(FileOffset at) {
143+
start_ = length_ = frame_ = 0;
144+
fileOffset_ = at;
145+
dirty_ = false;
146+
}
147+
148+
void DiscardLeadingBytes(std::size_t n, Terminator &terminator) {
149+
RUNTIME_CHECK(terminator, length_ >= n);
150+
length_ -= n;
151+
if (length_ == 0) {
152+
start_ = 0;
153+
} else {
154+
start_ += n;
155+
if (start_ >= size_) {
156+
start_ -= size_;
157+
}
158+
}
159+
if (frame_ >= n) {
160+
frame_ -= n;
161+
} else {
162+
frame_ = 0;
163+
}
164+
fileOffset_ += n;
165+
}
166+
167+
static constexpr std::size_t minBuffer{64 << 10};
168+
169+
char *buffer_{nullptr};
170+
std::size_t size_{0}; // current allocated buffer size
171+
FileOffset fileOffset_{0}; // file offset corresponding to buffer valid data
172+
std::int64_t start_{0}; // buffer_[] offset of valid data
173+
std::int64_t length_{0}; // valid data length (can wrap)
174+
std::int64_t frame_{0}; // offset of current frame in valid data
175+
bool dirty_{false};
176+
};
177+
}
178+
#endif // FORTRAN_RUNTIME_BUFFER_H_

runtime/environment.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===-- runtime/environment.cpp ---------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "environment.h"
10+
#include <cstdlib>
11+
#include <limits>
12+
13+
namespace Fortran::runtime {
14+
ExecutionEnvironment executionEnvironment;
15+
16+
void ExecutionEnvironment::Configure(
17+
int ac, const char *av[], const char *env[]) {
18+
argc = ac;
19+
argv = av;
20+
envp = env;
21+
listDirectedOutputLineLengthLimit = 79; // PGI default
22+
defaultOutputRoundingMode = common::RoundingMode::TiesToEven; // RP=RN
23+
24+
if (auto *x{std::getenv("FORT_FMT_RECL")}) {
25+
char *end;
26+
auto n{std::strtol(x, &end, 10)};
27+
if (n > 0 && n < std::numeric_limits<int>::max() && *end == '\0') {
28+
listDirectedOutputLineLengthLimit = n;
29+
} else {
30+
std::fprintf(
31+
stderr, "Fortran runtime: FORT_FMT_RECL=%s is invalid; ignored\n", x);
32+
}
33+
}
34+
35+
// TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment
36+
}
37+
}

runtime/environment.h

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===-- runtime/environment.h -----------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_RUNTIME_ENVIRONMENT_H_
10+
#define FORTRAN_RUNTIME_ENVIRONMENT_H_
11+
12+
#include "flang/common/Fortran.h"
13+
14+
namespace Fortran::runtime {
15+
struct ExecutionEnvironment {
16+
void Configure(int argc, const char *argv[], const char *envp[]);
17+
18+
int argc;
19+
const char **argv;
20+
const char **envp;
21+
int listDirectedOutputLineLengthLimit;
22+
common::RoundingMode defaultOutputRoundingMode;
23+
};
24+
extern ExecutionEnvironment executionEnvironment;
25+
}
26+
#endif // FORTRAN_RUNTIME_ENVIRONMENT_H_

0 commit comments

Comments
 (0)