Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9760096
Starting implementation of hostnm() non-standard intrinsic.
eugeneepshteyn Mar 13, 2025
828cd76
In progress
eugeneepshteyn Mar 14, 2025
b0a8798
Continue Hostnm() implementation
eugeneepshteyn Mar 18, 2025
f670146
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 18, 2025
a0b6f85
Code complete. Functional unit tests.
eugeneepshteyn Mar 18, 2025
b190979
Test for semantic checks of hostnm()
eugeneepshteyn Mar 18, 2025
e9fa37e
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 18, 2025
7cc9756
Documentation tweak
eugeneepshteyn Mar 18, 2025
843db5c
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 18, 2025
d3384ec
Fixed incorrect handling of status code
eugeneepshteyn Mar 19, 2025
b9f2d8f
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 19, 2025
f316320
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 19, 2025
4b61502
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 20, 2025
378e57f
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 20, 2025
42f7bc1
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 21, 2025
f9e8a39
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 23, 2025
21c0723
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 23, 2025
d2068a8
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 24, 2025
e375cc9
Addressed functional code review comments
eugeneepshteyn Mar 24, 2025
773055e
hostnm-sub.f90: hostnm() lowering test, subroutine form
eugeneepshteyn Mar 24, 2025
cb02a8e
Removed end-to-end tests
eugeneepshteyn Mar 24, 2025
d10fb71
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 24, 2025
633d0ba
Merge branch 'llvm:main' into hostnm-intrinsic
eugeneepshteyn Mar 25, 2025
e81f7f7
Forced to use GetComputerNameExA(), ASCII version of GetComputerNameE…
eugeneepshteyn Mar 25, 2025
719612a
Added hostnm-func.f90, a test for function form of hotnm()
eugeneepshteyn Mar 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions flang-rt/lib/runtime/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,48 @@ std::int32_t RTNAME(GetCwd)(
return status;
}

std::int32_t RTNAME(Hostnm)(
const Descriptor &res, const char *sourceFile, int line) {
Terminator terminator{sourceFile, line};

RUNTIME_CHECK(terminator, IsValidCharDescriptor(&res));

char buf[256];
std::int32_t status{0};

// Fill the output with spaces. Upon success, CopyCharsToDescriptor()
// will overwrite part of the string with the result, so we'll end up
// with a padded string. If we fail to obtain the host name, we return
// the string of all spaces, which is the original gfortran behavior.
FillWithSpaces(res);

#ifdef _WIN32

DWORD dwSize{sizeof(buf)};

// Note: Winsock has gethostname(), but use Win32 API GetComputerNameEx(),
// in order to avoid adding dependency on Winsock.
if (!GetComputerNameExA(ComputerNameDnsHostname, buf, &dwSize)) {
status = GetLastError();
}

#else

if (gethostname(buf, sizeof(buf)) < 0) {
status = errno;
}

#endif

if (status == 0) {
std::int64_t strLen{StringLength(buf)};
status = CopyCharsToDescriptor(res, buf, strLen);

// Note: if the result string is too short, then we'll return partial
// host name with "too short" error status.
}

return status;
}

} // namespace Fortran::runtime
44 changes: 38 additions & 6 deletions flang/docs/Intrinsics.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!--===- docs/Intrinsics.md
<!--===- docs/Intrinsics.md

Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

-->

# A categorization of standard (2018) and extended Fortran intrinsic procedures
Expand Down Expand Up @@ -703,7 +703,7 @@ CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, LOC
MALLOC, FREE
```

### Library subroutine
### Library subroutine
```
CALL BACKTRACE()
CALL FDATE(TIME)
Expand Down Expand Up @@ -961,7 +961,7 @@ program test_etime
call ETIME(tarray, result)
print *, result
print *, tarray(1)
print *, tarray(2)
print *, tarray(2)
do i=1,100000000 ! Just a delay
j = i * i - i
end do
Expand Down Expand Up @@ -1003,6 +1003,38 @@ PROGRAM example_getcwd
END PROGRAM
```

### Non-Standard Intrinsics: HOSTNM

#### Description
`HOSTNM(C, STATUS)` returns the host name of the system.

This intrinsic is provided in both subroutine and function forms; however, only one form can be used in any given program unit.

*C* and *STATUS* are `INTENT(OUT)` and provide the following:

| | |
|------------|---------------------------------------------------------------------------------------------------|
| `C` | The host name of the system. The type shall be `CHARACTER` and of default kind. |
| `STATUS` | (Optional) Status flag. Returns 0 on success, a system specific and nonzero error code otherwise. The type shall be `INTEGER` and of a kind greater or equal to 4. |

#### Usage and Info

- **Standard:** GNU extension
- **Class:** Subroutine, function
- **Syntax:** `CALL HOSTNM(C, STATUS)`, `STATUS = HOSTNM(C)`

#### Example
```Fortran
PROGRAM example_hostnm
CHARACTER(len=255) :: hnam
INTEGER :: status
CALL hostnm(hnam, status)
PRINT *, hnam
PRINT *, status
END PROGRAM
```


### Non-standard Intrinsics: RENAME
`RENAME(OLD, NEW[, STATUS])` renames/moves a file on the filesystem.

Expand Down Expand Up @@ -1088,7 +1120,7 @@ This intrinsic is provided in both subroutine and function forms; however, only
```Fortran
program chdir_func
character(len=) :: path
integer :: status
integer :: status

call chdir("/tmp")
status = chdir("..")
Expand Down
4 changes: 4 additions & 0 deletions flang/include/flang/Common/windows-include.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

// Target Windows 2000 and above. This is needed for newer Windows API
// functions, e.g. GetComputerNameExA()
#define _WIN32_WINNT 0x0500

#include <windows.h>

#endif // _WIN32
Expand Down
2 changes: 2 additions & 0 deletions flang/include/flang/Optimizer/Builder/IntrinsicCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ struct IntrinsicLibrary {
llvm::ArrayRef<mlir::Value> args);
mlir::Value genGetUID(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
fir::ExtendedValue genHostnm(std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
fir::ExtendedValue genIall(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genIany(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
Expand Down
5 changes: 5 additions & 0 deletions flang/include/flang/Optimizer/Builder/Runtime/Command.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,10 @@ mlir::Value genGetEnvVariable(fir::FirOpBuilder &, mlir::Location,
mlir::Value genGetCwd(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value c);

/// Generate a call to the Hostnm runtime function which implements
/// the HOSTNM intrinsic.
mlir::Value genHostnm(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value res);

} // namespace fir::runtime
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_COMMAND_H
4 changes: 4 additions & 0 deletions flang/include/flang/Runtime/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
// Calls getcwd()
std::int32_t RTNAME(GetCwd)(
const Descriptor &cwd, const char *sourceFile, int line);

// Calls hostnm()
std::int32_t RTNAME(Hostnm)(
const Descriptor &res, const char *sourceFile, int line);
}
} // namespace Fortran::runtime

Expand Down
3 changes: 3 additions & 0 deletions flang/include/flang/Runtime/extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ uid_t RTNAME(GetUID)();
// GNU extension subroutine GETLOG(C).
void FORTRAN_PROCEDURE_NAME(getlog)(char *name, std::int64_t length);

// GNU extension subroutine HOSTNM(C)
void FORTRAN_PROCEDURE_NAME(hostnm)(char *name, std::int64_t length);

std::intptr_t RTNAME(Malloc)(std::size_t size);

// GNU extension function STATUS = SIGNAL(number, handler)
Expand Down
12 changes: 11 additions & 1 deletion flang/lib/Evaluate/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"getgid", {}, DefaultInt},
{"getpid", {}, DefaultInt},
{"getuid", {}, DefaultInt},
{"hostnm",
{{"c", DefaultChar, Rank::scalar, Optionality::required,
common::Intent::Out}},
TypePattern{IntType, KindCode::greaterOrEqualToKind, 4}},
{"huge",
{{"x", SameIntUnsignedOrReal, Rank::anyOrAssumedRank,
Optionality::required, common::Intent::In,
Expand Down Expand Up @@ -1545,6 +1549,12 @@ static const IntrinsicInterface intrinsicSubroutine[]{
{"status", TypePattern{IntType, KindCode::greaterOrEqualToKind, 4},
Rank::scalar, Optionality::optional, common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"hostnm",
{{"c", DefaultChar, Rank::scalar, Optionality::required,
common::Intent::Out},
{"status", TypePattern{IntType, KindCode::greaterOrEqualToKind, 4},
Rank::scalar, Optionality::optional, common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"move_alloc",
{{"from", SameType, Rank::known, Optionality::required,
common::Intent::InOut},
Expand Down Expand Up @@ -2765,7 +2775,7 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
// Collection for some intrinsics with function and subroutine form,
// in order to pass the semantic check.
static const std::string dualIntrinsic[]{{"chdir"s}, {"etime"s}, {"getcwd"s},
{"rename"s}, {"second"s}, {"system"s}};
{"hostnm"s}, {"rename"s}, {"second"s}, {"system"s}};

return llvm::is_contained(dualIntrinsic, name);
}
Expand Down
35 changes: 35 additions & 0 deletions flang/lib/Optimizer/Builder/IntrinsicCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ static constexpr IntrinsicHandler handlers[]{
{"getgid", &I::genGetGID},
{"getpid", &I::genGetPID},
{"getuid", &I::genGetUID},
{"hostnm",
&I::genHostnm,
{{{"c", asBox}, {"status", asAddr, handleDynamicOptional}}},
/*isElemental=*/false},
{"iachar", &I::genIchar},
{"iall",
&I::genIall,
Expand Down Expand Up @@ -4317,6 +4321,37 @@ void IntrinsicLibrary::genGetEnvironmentVariable(
}
}

// HOSTNM
fir::ExtendedValue
IntrinsicLibrary::genHostnm(std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> args) {
assert((args.size() == 1 && resultType.has_value()) ||
(args.size() >= 1 && !resultType.has_value()));

mlir::Value res = fir::getBase(args[0]);
mlir::Value statusValue = fir::runtime::genHostnm(builder, loc, res);

if (resultType.has_value()) {
// Function form, return status.
return builder.createConvert(loc, *resultType, statusValue);
}

// Subroutine form, store status and return none.
const fir::ExtendedValue &status = args[1];
if (!isStaticallyAbsent(status)) {
mlir::Value statusAddr = fir::getBase(status);
mlir::Value statusIsPresentAtRuntime =
builder.genIsNotNullAddr(loc, statusAddr);
builder.genIfThen(loc, statusIsPresentAtRuntime)
.genThen([&]() {
builder.createStoreWithConvert(loc, statusValue, statusAddr);
})
.end();
}

return {};
}

/// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
/// take a DIM argument.
template <typename FD>
Expand Down
13 changes: 13 additions & 0 deletions flang/lib/Optimizer/Builder/Runtime/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,16 @@ mlir::Value fir::runtime::genGetCwd(fir::FirOpBuilder &builder,
builder, loc, runtimeFuncTy, cwd, sourceFile, sourceLine);
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
}

mlir::Value fir::runtime::genHostnm(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value res) {
mlir::func::FuncOp func =
fir::runtime::getRuntimeFunc<mkRTKey(Hostnm)>(loc, builder);
auto runtimeFuncTy = func.getFunctionType();
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
mlir::Value sourceLine =
fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(2));
llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
builder, loc, runtimeFuncTy, res, sourceFile, sourceLine);
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
}
23 changes: 23 additions & 0 deletions flang/test/Lower/Intrinsics/hostnm-func.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
!RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s

!CHECK-LABEL: func.func @_QPhostnm_test
!CHECK-SAME: %[[dummyHn:.*]]: !fir.boxchar<1> {fir.bindc_name = "hn"}) -> i32 {
integer function hostnm_test(hn)
CHARACTER(len=255) :: hn

! Check that _FortranAHostnm is called with boxed char 255, some other char
! string of variable length (source file path) and some integer (source line)
!CHECK-DAG: %[[func_result:.*]] = fir.alloca i32 {bindc_name = "hostnm_test", uniq_name = "_QFhostnm_testEhostnm_test"}
!CHECK-DAG: %[[func_result_decl:.*]]:{{.*}} = hlfir.declare %[[func_result]] {uniq_name = "_QFhostnm_testEhostnm_test"} : {{.*}}fir.ref<i32>{{.*}}
!CHECK-DAG: %[[line:.*]] = arith.constant {{.*}} : i32
!CHECK-DAG: %[[hn:.*]] = fir.convert {{.*}} (!fir.box<!fir.char<1,255>>) -> !fir.box<none>
!CHECK-DAG: %[[src_path:.*]] = fir.convert {{.*}} (!fir.ref<!fir.char<1,{{.*}} -> !fir.ref<i8>
!CHECK: %[[hn_result:.*]] = fir.call @_FortranAHostnm(%[[hn]], %[[src_path]], %[[line]])
!CHECK-SAME: -> i32

! Check _FortranAHostnm result code handling
!CHECK-DAG: hlfir.assign %[[hn_result]] to %[[func_result_decl]]{{.*}}i32{{.*}}
!CHECK-DAG: %[[load_result:.*]] = fir.load %[[func_result_decl]]{{.*}}i32{{.*}}
!CHECK: return %[[load_result]] : i32
hostnm_test = hostnm(hn)
end function hostnm_test
38 changes: 38 additions & 0 deletions flang/test/Lower/Intrinsics/hostnm-sub.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
!RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s

!CHECK-LABEL: func.func @_QPhostnm_only
!CHECK-SAME: %[[dummyHn:.*]]: !fir.boxchar<1> {fir.bindc_name = "hn"}) {
subroutine hostnm_only(hn)
CHARACTER(len=255) :: hn

! Check that _FortranAHostnm is called with boxed char 255, some other char
! string of variable length (source file path) and some integer (source line)
!CHECK-DAG: %[[line:.*]] = arith.constant {{.*}} : i32
!CHECK-DAG: %[[hn:.*]] = fir.convert {{.*}} (!fir.box<!fir.char<1,255>>) -> !fir.box<none>
!CHECK-DAG: %[[src_path:.*]] = fir.convert {{.*}} (!fir.ref<!fir.char<1,{{.*}} -> !fir.ref<i8>
!CHECK: fir.call @_FortranAHostnm(%[[hn]], %[[src_path]], %[[line]])
!CHECK-SAME: -> i32
call hostnm(hn)
end subroutine hostnm_only

!CHECK-LABEL: func.func @_QPall_arguments
!CHECK-SAME: %[[dummyHn:.*]]: !fir.boxchar<1> {fir.bindc_name = "hn"},
!CHECK-SAME: %[[dummyStat:.*]]: !fir.ref<i32> {fir.bindc_name = "status"}) {
subroutine all_arguments(hn, status)
CHARACTER(len=255) :: hn
INTEGER :: status

! Check that _FortranAHostnm is called with boxed char 255, some other char
! string of variable length (source file path) and some integer (source line)
!CHECK-DAG: %[[line:.*]] = arith.constant {{.*}} : i32
!CHECK-DAG: %[[hn:.*]] = fir.convert {{.*}} (!fir.box<!fir.char<1,255>>) -> !fir.box<none>
!CHECK-DAG: %[[src_path:.*]] = fir.convert {{.*}} (!fir.ref<!fir.char<1,{{.*}} -> !fir.ref<i8>
!CHECK: %[[hn_result:.*]] = fir.call @_FortranAHostnm(%[[hn]], %[[src_path]], %[[line]])
!CHECK-SAME: -> i32

! Check _FortranAHostnm result code handling
!CHECK-DAG: %[[c0_i64:.*]] = arith.constant 0 : i64
!CHECK-DAG: %[[cmp_result:.*]] = arith.cmpi ne, {{.*}}, %[[c0_i64]] : i64
!CHECK: fir.store %[[hn_result]] {{.*}} !fir.ref<i32>
call hostnm(hn, status)
end subroutine all_arguments
42 changes: 42 additions & 0 deletions flang/test/Semantics/hostnm.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic
! Tests for the HOSTNM intrinsics.

subroutine bad_kind_error(cwd, status)
CHARACTER(len=255) :: cwd
INTEGER(2) :: status
!ERROR: Actual argument for 'status=' has bad type or kind 'INTEGER(2)'
call hostnm(cwd, status)
end subroutine bad_kind_error

subroutine bad_args_error()
!ERROR: missing mandatory 'c=' argument
call hostnm()
end subroutine bad_args_error

subroutine bad_function(cwd)
CHARACTER(len=255) :: cwd
INTEGER :: status
call hostnm(cwd, status)
!ERROR: Cannot call subroutine 'hostnm' like a function
status = hostnm(cwd)
end subroutine bad_function

subroutine bad_sub(cwd)
CHARACTER(len=255) :: cwd
INTEGER :: status
status = hostnm(cwd)
!ERROR: Cannot call function 'hostnm' like a subroutine
call hostnm(cwd, status)
end subroutine bad_sub

subroutine good_subroutine(cwd, status)
CHARACTER(len=255) :: cwd
INTEGER :: status
call hostnm(cwd, status)
end subroutine good_subroutine

subroutine good_function(cwd, status)
CHARACTER(len=255) :: cwd
INTEGER :: status
status = hostnm(cwd)
end subroutine good_function