コグノスケ


2020年6月1日

GCCを調べる - その13-4 - ベクトル命令のオフセット付きアドレスだけを禁止

目次: GCC

前回(2020年5月31日の日記参照)見たとおり、メモリというかオフセット付きアドレスを全部禁止するのは明らかにやりすぎで、他の命令に悪影響を及ぼしていました。スカラ命令はオフセット付きアドレスを許可し、ベクトル命令だけオフセット付きアドレスを禁止したいところです。

オペランドを許可する、しないを判断するコードを改造すればできるでしょうか?コードを見てみます。

constraint "m" の定義(再掲)

// gcc/common.md

(define_memory_constraint "TARGET_MEM_CONSTRAINT"
  "Matches any valid memory."
  (and (match_code "mem")
       (match_test "memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0),
						 MEM_ADDR_SPACE (op))")))
constraint "m" の条件判定コード

// gcc/recog.c

//★★各引数の値
//mode = E_V64SImode
//addr = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//as = 0

int
memory_address_addr_space_p (machine_mode mode ATTRIBUTE_UNUSED,
			     rtx addr, addr_space_t as)
{
#ifdef GO_IF_LEGITIMATE_ADDRESS
...
#else
  return targetm.addr_space.legitimate_address_p (mode, addr, 0, as);
#endif
}


// gcc/targhook.c

//★★各引数の値
//mode = E_V64SImode
//mem  = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//strict = 0
//as = 0

bool
default_addr_space_legitimate_address_p (machine_mode mode, rtx mem,
					 bool strict,
					 addr_space_t as ATTRIBUTE_UNUSED)
{
  return targetm.legitimate_address_p (mode, mem, strict);
}


// gcc/config/riscv/riscv.c

#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P	riscv_legitimate_address_p


// gcc/config/riscv/riscv.c

//★★各引数の値
//mode = E_V64SImode
//mem  = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//strict_p = 0

static bool
riscv_legitimate_address_p (machine_mode mode, rtx x, bool strict_p)
{
  struct riscv_address_info addr;

  return riscv_classify_address (&addr, x, mode, strict_p);
}


// gcc/config/riscv/riscv.c

//★★各引数の値
//x    = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//mode = E_V64SImode
//strict_p = 0

static bool
riscv_classify_address (struct riscv_address_info *info, rtx x,
			machine_mode mode, bool strict_p)
{
  switch (GET_CODE (x))
    {

...

    case PLUS:
      info->type = ADDRESS_REG;
      info->reg = XEXP (x, 0);
      info->offset = XEXP (x, 1);

      //★★各変数の値
      //info->reg    = x->u.fld[0].rt_rtx = (reg/f:SI 108)
      //info->offset = x->u.fld[1].rt_rtx = (const_int 256 [0x100])

      return (riscv_valid_base_register_p (info->reg, mode, strict_p)
	      && riscv_valid_offset_p (info->offset, mode));

...

    }
}

関数memory_address_addr_space_p() のaddrを見ると、メモリアドレスを表すRTLしか渡されません。この情報だけではスカラ命令のオペランドか、ベクトル命令のオペランドか、判断するのは困難です。

ベクトル命令はmachine modeがV64SIであることを利用するとうまくいくかもしれません。関数riscv_classify_address() を変更し、mode == V64SIだったらPLUSなどREG以外を使ったRTLに対しfalseを返せば良さそうです。

mode == V64SIのときfalseを返す

// gcc/config/riscv/riscv.c

//★★各引数の値
//x    = (plus:SI (reg/f:SI 108)
//           (const_int 256 [0x100]))
//mode = E_V64SImode
//strict_p = 0

static bool
riscv_classify_address (struct riscv_address_info *info, rtx x,
			machine_mode mode, bool strict_p)
{
  switch (GET_CODE (x))
    {

...

    case PLUS:
      if (mode == E_V64SImode)  //★★machine modeがV64SIならオフセット付きアドレスは許可しない
        return false;

      info->type = ADDRESS_REG;
      info->reg = XEXP (x, 0);
      info->offset = XEXP (x, 1);

      return (riscv_valid_base_register_p (info->reg, mode, strict_p)
	      && riscv_valid_offset_p (info->offset, mode));

...

    }
}
mode == V64SIのときfalseを返す変更を加えたとき、アセンブラの差分

$ diff -u b_before.s b_mod.s

--- b_before.s  2020-05-28 21:17:24.607184754 +0900
+++ b_mod.s     2020-05-30 22:37:02.370663628 +0900
@@ -35,7 +35,8 @@
 
 # 0 "" 2
  #NO_APP
-       vsw.v   v0,256(a5)
+       addi    a4,a5,256
+       vsw.v   v0,0(a4)
        .loc 1 9 2
        addi    a4,s0,-336
  #APP

結果だけ見ると良さそうですが、将来的にオフセットアドレスが使えるベクトル命令が出てきたときに、判別不能になり困ります。その場しのぎ感が否めません。もっと良い方法はあるでしょうか?

もっと簡単な方法

実はもっと簡単な方法で対処できます。変更すべき箇所については、この取り組みの発端「constraint "m" をチェックしていそうな箇所から調査を開始した」ことを思い出していただければ想像が付くと思います。constraint "m" を使っている箇所はdefine_insnです。

追加したdefine_insn(再掲)

;; gcc/config/riscv/riscv.md

(define_attr "vecmode" "unknown,V64SI"
  (const_string "unknown"))

(define_insn "*movv64si_internal"
  [(set (match_operand:V64SI 0 "nonimmediate_operand" "=v,v,m")
	(match_operand:V64SI 1 "move_operand"         " v,m,v"))]    //★★constraint "m" を使っている場所
  "(register_operand (operands[0], V64SImode)
    || reg_or_0_operand (operands[1], V64SImode))" return riscv_output_move (operands[0], operands[1]); 
  [(set_attr "move_type" "move,load,store")
   (set_attr "vecmode" "V64SI")])

この "m" を変更して、オフセット付きアドレスオペランドだけを拒否したいですが、そんな都合の良いconstraintはあるでしょうか?実はRISC-Vには既にあります。constraint "A" です。

constraint "A" の定義

;; gcc/config/riscv/constraints.md

(define_memory_constraint "A"
  "An address that is held in a general-purpose register."
  (and (match_code "mem")
       (match_test "GET_CODE(XEXP(op,0)) == REG")))

コードを見ると、constraint "A" が見ている条件は、種別がメモリであり、オペランドがREG(オフセットありだとPLUSなどになる)であることです。先程の改造と同じ発想ですね。mをAに変更します。

変更後のdefine_insn

;; gcc/config/riscv/riscv.md

(define_insn "*movv64si_internal"
  [(set (match_operand:V64SI 0 "nonimmediate_operand" "=v,v,A")
	(match_operand:V64SI 1 "move_operand"         " v,A,v"))]    //★★constraint "A" に変更
...
constraint "A" を使ったとき、アセンブラの差分

$ diff -u b_before.s b_final.s

--- b_before.s  2020-05-28 21:17:24.607184754 +0900
+++ b_final.s   2020-05-28 21:20:20.219175573 +0900
@@ -35,7 +35,8 @@
 
 # 0 "" 2
  #NO_APP
-       vsw.v   v0,256(a5)
+       addi    a4,a5,256
+       vsw.v   v0,0(a4)
        .loc 1 9 2
        addi    a4,s0,-336
  #APP

出力結果のアセンブリも良い感じですし、ビルドの際にアセンブラも文句を言わなくなりました。

長きに渡りましたが、やっとベクトル型を使ったGCC拡張インラインアセンブラが書けるようになりました。良かった良かった。

編集者:すずき(2023/09/24 11:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月2日

GCC - まとめリンク

目次: GCC

GCCについて。

GCCを含めたツールチェーンについて。

編集者:すずき(2024/03/06 00:06)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月3日

GCCを調べる - その14-1 - genopinitとexpandとenum optab_tag

目次: GCC

以前(2020年5月24日の日記参照)、変数の代入操作をexpandで展開する際、代入を分割するか分割しないか、を決める条件の1つとして、optab_handler() という関数が出てきました。optab_handler() の動作を決めるgenopinitというツールの動作を調べます。

バイナリはbuild_gcc/build/genopinitに生成されます(以降build_gccはGCCのビルドディレクトリを指すものとします)。実行する際の引数は下記の通りです。

genopinitの使い方(再掲)

# build_gcc/Makefile

s-opinit: $(MD_DEPS) build/genopinit$(build_exeext) insn-conditions.md
	$(RUN_GEN) build/genopinit$(build_exeext) $(md_file) \
	  insn-conditions.md -htmp-opinit.h -ctmp-opinit.c


# ビルドログ

build/genopinit ./gcc/gcc/common.md ./gcc/gcc/config/riscv/riscv.md \
  insn-conditions.md -htmp-opinit.h -ctmp-opinit.c

上記のビルドログでは一時ファイルに出力していますが、最終的にはbuild_gcc/insn-opinit.h, build_gcc/insn-opinit.cの2つのファイルを生成します。

genopinitとenum optab_tagの生成

前回はoptab_handler() にブレークポイントを設定するため、enum optab_tagの値を使いました。この値はgenopinitが生成しています。

optab_handler(再掲、前半のみ)

// gcc/optabs-query.h

/* Return the insn used to implement mode MODE of OP, or CODE_FOR_nothing
   if the target does not have such an insn.  */

inline enum insn_code
optab_handler (optab op, machine_mode mode)
{
  unsigned scode = (op << 16) | mode;  //★★以前はop = mov_optab, mode = E_V64SImodeでブレークを掛けた
  gcc_assert (op > LAST_CONV_OPTAB);
  return raw_optab_handler (scode);  //★★次回以降、調べます
}


// build_gcc/insn-opinit.h

enum optab_tag {
  unknown_optab,
  sext_optab,

...

  mov_optab,  //★★この値のこと

...

};

...

typedef enum optab_tag optab;

これらの値はgensupport.cのoptab_defから生成されています。実際にoptab_defの中身を定義するのはoptabs.defファイルです。この *.defファイルは、C言語で素直に書くと重複、冗長になる情報を簡潔に表すための、いわゆるDSL(Domain Specific Language)だと思われます。

DSLっぽいものを使うとき、下記のような邪悪なincludeの使い方をします。GCC凶悪デザインパターンの1つですね。良い子は真似してはいけません。

optab_handler(再掲)

// gcc/gensupport.c

#define OPTAB_DC(o, p, c)          { #o, p, NS, ZS, NS, o, c, c, 4 },
#define OPTAB_D(o, p)  { #o, p, NS, ZS, NS, o, UNKNOWN, UNKNOWN, 4 },

...

/* An array of all optabs.  Note that the same optab can appear more
   than once, with a different pattern.  */
optab_def optabs[] = {
  { "unknown_optab", NULL, NS, ZS, NS, unknown_optab, UNKNOWN, UNKNOWN, 0 },
#include "optabs.def"  //★★上記のようにoptabs.def内で使われるマクロを、都合の良い定義に変えてからinclude
};


// gcc/optabs.def

OPTAB_DC(mov_optab, "mov$a", SET)  //★★マクロの定義はincludeされる場所によって違うので、展開後の結果は場所による
OPTAB_DC(movstrict_optab, "movstrict$a", STRICT_LOW_PART)
OPTAB_D (movmisalign_optab, "movmisalign$a")

...


// gcc/genopinit.c

int
main (int argc, const char **argv)
{

...

  /* Emit the optab enumeration for the header file.  */
  fprintf (h_file, "enum optab_tag {\n");
  for (i = j = 0; i < n; ++i)
    {
      optabs[i].op = i;
      fprintf (h_file, "  %s,\n", optabs[i].name);  //★★optabsの名前を出力
      if (optabs[i].kind != j)
	last_kind[j++] = i - 1;
    }
  fprintf (h_file, "  FIRST_CONV_OPTAB = %s,\n", optabs[last_kind[0]+1].name);

...

せっかくDSLっぽいものがあるのに、genopinitのようにコード自動生成ツールも混ぜて使うのはどうしてなんでしょう?GCCは理解不能です。

編集者:すずき(2023/09/24 11:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月4日

GCCを調べる - その14-2 - genopinitとexpandとpats[].scode

目次: GCC

以前(2020年5月24日の日記参照)、変数の代入操作をexpandで展開する際、代入を分割するか分割しないか、を決める条件の1つとして、optab_handler() という関数が出てきました。この関数の動作に関わるgenopinitというツールの動作を調べます。

前回(2020年6月3日の日記参照)は分岐条件に関わるenum optab_tagの生成について調べました。今回はもう少し複雑なpats[].scodeの生成について調べます。

pats[].scodeって何だ?

突然pats[].scodeと言われても何だかわからないと思います。私もこんなひどい名前の変数、全く覚えられません。復習も兼ねてoptab_handler() を見直します。

分岐条件の1つoptab_handler()(再掲)

// gcc/optabs-query.h

/* Return the insn used to implement mode MODE of OP, or CODE_FOR_nothing
   if the target does not have such an insn.  */

inline enum insn_code
optab_handler (optab op, machine_mode mode)
{
  unsigned scode = (op << 16) | mode;  //★★scodeの意味はここにある通り
  gcc_assert (op > LAST_CONV_OPTAB);
  return raw_optab_handler (scode);  //★★これ
}


// build_gcc/insn-opinit.c

enum insn_code
raw_optab_handler (unsigned scode)
{
  int i = lookup_handler (scode);  //★★これ
  return (i >= 0 && this_fn_optabs->pat_enable[i]
          ? pats[i].icode : CODE_FOR_nothing);
}

static int
lookup_handler (unsigned scode)
{
  int l = 0, h = ARRAY_SIZE (pats), m;
  while (h > l)
    {
      m = (h + l) / 2;
      if (scode == pats[m].scode)  //★★これ
        return m;
      else if (scode < pats[m].scode)  //★★これ
        h = m;
      else
        l = m + 1;
    }
  return -1;
}


//★★pats[] の定義

struct optab_pat {
  unsigned scode;
  enum insn_code icode;
};

static const struct optab_pat pats[NUM_OPTAB_PATTERNS] = {
  { 0x010405, CODE_FOR_extendqihi2 },
  { 0x010406, CODE_FOR_extendqisi2 },
  { 0x010407, CODE_FOR_extendqidi2 },
  { 0x010505, CODE_FOR_extendhihi2 },

...

  { 0x021c1b, CODE_FOR_truncdfsf2 },  //★★この変な数字(scode)0x021c1bは誰が作るのか?

...

コードからscodeの意味、上位16ビットがoptabで、下位16ビットがmachine mode、は理解できると思います。patsにはscodeが無数に並んでいますが、この数字を誰が作るかというとgenopinitです。

optabsとpattern

前に見たoptabsの定義は色々ありますが、必ず2つ以上の引数を取り、1番目がname(mov_optab, trunc_optabなど)、2番目がpatternとなっており、この2つは全ての定義に存在します。

optabsの定義とname, pattern

// gcc/optabs.def

OPTAB_CL(trunc_optab, "trunc$b$a2", TRUNCATE, "trunc", gen_trunc_conv_libfunc)
         '-- name      `-- pattern

このうちnameはenum optab_tagの変数名として使っていました。その他にもcode_to_optab_[] という配列の定義にも使いますが、今はどうでもいいです。前後に文字が付くくらいで、基本的に名前がそのまま使われます。

一方2番目のpattern(例えば "mov$a", "trunc$b$a2" など)はちょっと変わっています。$aや $bという不思議な文字が入ります。

genopinitと *.mdとscode

前回説明したとおりgenopinitの入力はgcc/common.md, gcc/config/riscv/riscv.md, build_gcc/insn-conditions.mdの3つの *.mdファイルです(mdはmachine descriptorの略)。Lispっぽい記法で、define_insnやdefine_expandを追加したファイルです。

基本的なgenopinitの動作は、

  • *.mdを読む
  • optabs.defのpatternとパターンマッチを掛け、pats[].scodeを生成
  • ファイルに出力

パターンマッチのコードはこんな感じです。

genopinitパターンマッチ部分

// gcc/gensupport.h

/* Information about an .md define_* rtx.  */
class md_rtx_info {
public:
  /* The rtx itself.  */
  rtx def;

  /* The location of the first line of the rtx.  */
  file_location loc;

  /* The unique number attached to the rtx.  Currently all define_insns,
     define_expands, define_splits, define_peepholes and define_peephole2s
     share the same insn_code index space.  */
  int index;
};


// gcc/genopinit.c

int
main (int argc, const char **argv)
{

...

  if (!init_rtx_reader_args_cb (argc, argv, handle_arg))  //★★ *.mdを解析
    return (FATAL_EXIT_CODE);

...

  /* Read the machine description.  */
  md_rtx_info info;
  while (read_md_rtx (&info))  //★★RTXを1つずつ取り出す
    switch (GET_CODE (info.def))
      {
      case DEFINE_INSN:    //★★define_insn, define_expandだけ注目
      case DEFINE_EXPAND:
	gen_insn (&info);  //★★これ
	break;


static void
gen_insn (md_rtx_info *info)
{
  optab_pattern p;
  if (find_optab (&p, XSTR (info->def, 0)))  //★★これ
    patterns.safe_push (p);
}


// gcc/gensupport.c

bool
find_optab (optab_pattern *p, const char *name)
{

...

  /* See if NAME matches one of the patterns we have for the optabs
     we know about.  */
  for (unsigned int pindex = 0; pindex < ARRAY_SIZE (optabs); pindex++)  //★★全てのpatternを試す
    {
      p->m1 = p->m2 = 0;
      if (match_pattern (p, name, optabs[pindex].pattern))  //★★これ
	{
	  p->name = name;
	  p->op = optabs[pindex].op;
	  p->sort_num = (p->op << 16) | (p->m2 << 8) | p->m1;  //★★scodeを作る
	  return true;
	}


// gcc/gensupport.h

/* Information about an instruction name that matches an optab pattern.  */
struct optab_pattern
{
  /* The name of the instruction.  */
  const char *name;

  /* The matching optab.  */
  unsigned int op;

  /* The optab modes.  M2 is only significant for conversion optabs;
     it is zero otherwise.  */
  unsigned int m1, m2;

  /* An index that provides a lexicographical sort of (OP, M2, M1).
     Used by genopinit.c.  */
  unsigned int sort_num;
};

申し訳ないですが *.mdファイルの解析関数init_rtx_reader_args_cb(), read_md_rtx() 辺りは調べる予定がありません。どなたか調べてくれたら嬉しいです。

パターンマッチはnameに対して行われ、全てのpatternとマッチするか試します。マッチしなければmatch_pattern() はfalseを返しますから、次のpatternを試します。マッチしたら、結果はoptab_pattern *pに格納され、match_patter() が作り出すp->m1, p->m2という謎の数からscodeが生成されます。

なぜgenopinitではsort_numという変数名にしたんでしょうね?scodeにすればもう少しわかりやすいのに。

パターンマッチm1, m2とscode

パターンマッチのルールと、m1, m2が何者か?については、文章で説明できる気がしないので、例としてname = "truncdfsf2" を見ながら説明したいと思います。マッチするpatternは "trunc$b$a2" です。他のpatternはマッチしません。

genopinitパターンマッチの一例

// gcc/config/riscv/riscv.md

//★★nameの例

(define_insn "truncdfsf2"    ★★名前
  [(set (match_operand:SF     0 "register_operand" "=f")
	(float_truncate:SF
	    (match_operand:DF 1 "register_operand" " f")))]
  "TARGET_DOUBLE_FLOAT"
  "fcvt.s.dt%0,%1"
  [(set_attr "type" "fcvt")
   (set_attr "mode" "SF")])


// gcc/optab.def

//★★ マッチするpatternの定義部分(2番目の引数)

OPTAB_CL(trunc_optab, "trunc$b$a2", TRUNCATE, "trunc", gen_trunc_conv_libfunc)


// gcc/gensupport.c

//★★
//pは結果格納用の変数
//name = "truncdfsf2"
//pat  = "trunc$b$a2"

static bool
match_pattern (optab_pattern *p, const char *name, const char *pat)
{

...
パターンマッチしたときの各変数のダンプ
★★match_pattern() がtrueを返した後、find_optab() がreturn trueで終了する直前でダンプ

(gdb) p optabs[pindex]

$13 = {
  name = 0x4574b5 "trunc_optab",
  pattern = 0x4574c1 "trunc$b$a2",
  base = 0x4574cc ""trunc"",
  suffix = 0x457478 "'\0'",
  libcall = 0x4574d4 "gen_trunc_conv_libfunc",
  op = 2,
  fcode = TRUNCATE,
  rcode = UNKNOWN,
  kind = 1
}


(gdb) p/x *p

$15 = {
  name = 0x4dd270,
  op = 0x2,
  m1 = 0x1b,
  m2 = 0x1c,
  sort_num = 0x21c1b
}

pは結果格納用のoptab_pattern *p

この定義のpatternは2番目の引数 "trunc$b$a2" です。全部繋がっていてわかりにくいですが、下記の4つの要素から構成されます。

  • trunc
  • $b: パターンマッチ その2
  • $a: パターンマッチ その1
  • 2

このうち $aや $bはmachine modeの名前にマッチします。具体的には下記のようになります。

$a, $bパターンマッチの仕組み
nameとpatが

  name = "truncdfsf2"
  pat  = "trunc$b$a2"

の場合、

  $b -> df
  $a -> sf

にマッチします。$a, $bのマッチを調べるときはmode_nameを先頭から検索(大文字小文字の違いは無視)します。
全モードの名前はmode_name[] という配列に入っています。

  $bはmode_name[28] = "DF" と一致する。
  $aはmode_name[27] = "SF" と一致する。

match_pattern() は一致したモードの番号をm1, m2に格納します。
つまりoptab_patternのm1, m2の意味はそれぞれ

  m1: $aがマッチしたモードのインデックス、今回だと27 = 0x1b (E_SFmode)
  m2: $bがマッチしたモードのインデックス、今回だと28 = 0x1c (E_DFmode)

結果scodeはこうなります。

  p->sort_num = (p->op << 16) | (p->m2 << 8) | p->m1;
              = (2 << 16) | (28 << 8) | 27;
              = 0x21c1b

// build_gcc/insn-opinit.c

static const struct optab_pat pats[NUM_OPTAB_PATTERNS] = {
...
  { 0x021c1b, CODE_FOR_truncdfsf2 },  //★★この値が生成された

謎のpats[].scodeはこんな仕組みで生成されていたのでした。ややこしいですね。

編集者:すずき(2023/09/24 11:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月5日

FSF Copyright Assignment

目次: GCC

FSF (Free Software Foundation) のCopyright Assignmentにサインしてみました。とりあえずGCCとbinutilsです。ツールチェーン仲間としてはglibcもやっておけば良かったかも??まあいいか。

手続きは特に難しくなく、

  • assign@gnu.orgに特定のテンプレート(※)でメールを送る
  • 契約書が添付されたPDFが返ってくる
  • 印刷してサイン+日付を書いた後、スキャンしてPDFを送る
  • FSFのサイン+日付が入ったPDFが送られてくる

これで完了です。


送られてくる契約書の先頭部分

FSFからの返事はそれぞれ数日〜1週間程度、という感じでした。GNUのソフトウェアにパッチを送りたいときは、個人でこの契約を交わす(今回はこっち)か、会社の業務で作成している場合は、会社でこの契約を交わす(こっちのやり方は知らない)必要があります。

FSFがなぜこんな面倒なことをしているのかについては、なぜFSFは貢献者に著作権の譲渡をお願いしているのか - GNUプロジェクトに書いてあります。過去に訴えられたり何か嫌なことがあったんでしょうね。

テンプレートは Copyright Papers (Information for Maintainers of GNU Software) を見るとGNUのメンテナーに要求してくれと書いてあります。gnulibのリポジトリにも入っていて、私はこのファイルを送ったらOKでした(gnulibのgitリポジトリにあるrequest-assign.future

Copyright Assignmentを読んでみて感じたこと

FSFのCopyright Assignmentにはいろいろ書いてありますが、

契約者の書いたパッチが契約者のものだと保証せよ
これは契約者を雇っている会社がFSFに著作権を主張してくることを防ごうとしている。
業務上作成したパッチを投稿しなければ満たせる。
FSFの敵に対して協力せよ
契約者が上記の項に反した(会社のコードを投稿したなど)ときに要求されます。
FSFが裁判で勝てるような証拠集めに協力せよ。
FSFが万が一、著作権関係の訴えで負けたら「FSFの被った損害を補償せよ」

読んでいると割と怖い内容ですが、会社で書いたコードを投稿しなければ問題はないです。

契約の範囲はソフトウェア毎(GCC, binutils, glibc, などなど)に契約する必要があります。契約の種類は「1回限り(request-assign.changes)」と「今後ずっと(request-assign.future)」があります。ソフトウェアを改善し続ける場合、毎回契約するのは面倒なので「今後ずっと」を選択すると思います。

契約の種類によってFSFに送るメールのテンプレートが異なります。詳細は Copyright Papers (Information for Maintainers of GNU Software) に書いてあります。和訳ないのかなあ、これ。

編集者:すずき(2023/09/24 11:44)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月6日

Kindle Fireの本が消える病

目次: Kindle

割と昔からですがKindle Fireがストアで買った本を認識しない(ダウンロードしない、一覧に出てこない)病気が頻発し、買ったはずの本が消えて、非常に困っています。

Kindle Fireがロストした本は、Kindle for PCだとあっさり捕捉できますから(「新しい商品」順に並べると確認しやすい)、Kindle Fireのバグだと思われます。

根本対策は不明ですが、対症療法はいくつかあります。お手軽な順に、

  • 本の一覧を表示、フィルター「すべて」「ダウンロード済み」を何度か切り替える(3割くらい成功)
  • 対象の本のタイトルを検索ウインドウに入れて検索する(8割くらい成功)
  • Kindle ストアで対象の本を探し「ダウンロード」ボタンを押す(9割9分成功)
  • ファクトリリセット(これでだめならKindle Fireのバグ)

Kindleストアも割と曲者で、複数のダウンロードボタンがある変な仕様2018年11月17日の日記参照)です。1回押してダメでも諦めず、他の種類のダウンロードボタンも押してください。どれかが成功すればラッキーです。

最終手段ファクトリリセットには1度だけお世話になりました。しかし再設定、再ダウンロードにかなり時間を浪費して辛いので、できればもう二度とやりたくありません。

Kindle Fireの困ったところ

Kindle Fireはちょっと変な挙動が多いです。仕様かバグか良くわからないものもあります。

  • 本の表示がずれて読めなくなる(再起動で直る)
  • 本がダウンロードできない病が頻発する(上述)
  • 本の一覧表示が遅い(アップデートで改悪したり、改善したり)
  • 一覧表示の分類がショボい、本が探せない
  • フィルタの「未読」「既読」の数が間違ってる

Kindleのメインであるはずの本の機能さえ、この有様なので、本以外(アプリ、ミュージック、ムービーなど)の機能は一体どうなっていることやら??

メモ: 技術系の話はFacebookから転記しておくことにした。大幅に追記。

編集者:すずき(2021/12/08 04:02)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月12日

懐かしいソフトをインストール

はるか昔(2007年6月7日の日記参照)に購入したピアノ音源PMI Boesendorfer 290を現在使っているPCにインストールしました。

当時の日本代理店はクリプトンでしたが、2008年に違う会社に変わったようです。

それはさておき、ソフトは古くても(13年前!)音源自体はインストールでき、音も正常に鳴ります。Windowsの後方互換性は本当に素晴らしいなと思います。しかしアクティベーションだけがどうしてもできなくて困っています。

オンラインでアクティベートできず

Boesendorfer 290はインストール時にシリアル番号の入力が必要で、インストール後はアクティベーションが必要です。アクティベーションは各PC固有の番号と紐付いていて、別のPCにインストールしたときは再アクティベーションが必要となります。面倒くさいですね。

アクティベーションをしない状態で使い続けると、インストール後5日間に動かなくなってしまいます。


Boesendorfer 290アクティベート画面

購入当時はアクティベート画面の「REGISTER NOW」を押せば、オンラインでアクティベーションできましたが、現在このボタンを押すとエラーになります。


オンラインのアクティベーション、エラーその1


オンラインのアクティベーション、エラーその2

アクティベーション用のサイトのURLが変わったのか、ブラウザの接続警告が出ますし、転送先のサイトでは「そんなページは知らない」って言われます。これはひどい。

オフラインでもアクティベートできず

オンラインがダメならオフラインアクティベーションを試そうと思い「FILL OUT FORM」を押してみましたが、やっぱりダメでした。


オフラインのアクティベーション、エラー

エラーメッセージを見てもInternet browserの何がダメかわかりません。当然、ユーザーとしても対処しようがありません。このソフトを開発した人たちも、まさか13年後にアクティベーションする奴がいるとは思っていなかったでしょう。

これ以上どうにもならないので、サポートに連絡することにしました。Boesendorfer 290の販売はNative Instruments社、開発はEastWest社とありますが、どこに連絡すれば良いかわかりません。とりあえず販売Native Instruments社のサポートに連絡してみようと思います。

編集者:すずき(2020/06/21 14:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月19日

Boesendorfer 290のインストール、続き

以前(2020年6月12日の日記参照)にインストール&アクティベートで失敗したBoesendorfer 290ですが、Native Instrumentsのサポートの方とのQAにより、インストール方法がわかりました。

カギはNative Accessというツールと、Kontakt Playerというアプリケーションです。

Native Accessはインストーラ&情報管理ツールです。アップデートもお知らせしてくれるようです。動きに癖があり、やや使いにくいです。

Kontakt Playerは音源データから音色を合成するプレイヤーです。なんと無料、凄い。私はプレイヤーとしてしか使っていませんが、本来はもっと高機能なのかな?

私の理解が正しければ、Boesendorfer 290にはピアノ音源データと、音色合成プレイヤーの2つがセットになっていますが、音色合成プレイヤーはもはやアクティベーション不可能で利用できません。代わりにKontakt 6 Playerを使うのが正解です。

Native Accessのインストール

最初にNative Accessをインストールします。インストーラを起動するだけです。このアプリはたまにアップデート確認画面から進まなくなることがあります。私の場合、Native Accessを再起動するか、Windowsを再起動したら直りました。


Native Access起動時のアップデート確認画面

Kontakt 6 Playerをインストールしようとすると、こんな画面になると思います。


インストール失敗

これがNative Accessの良くないところですね……、ディレクトリの設定に失敗しているとこのエラーになりますが、エラーメッセージを見ても、何が悪いのか全くわからないです。


ディレクトリ設定メニュー


ディレクトリ設定画面

Preferencesというメニューからディレクトリの設定ができます。パスを指定するだけではダメで、ディレクトリが存在していないとさっきのエラーが出ます。パスの最後のディレクトリがなかったら、勝手に作ってくれれば良いのに。これもNative Accessのイマイチなところです……。

Kontakt 6 Playerのインストール

次にKontakt 6 Playerをインストールします。


Kontakt 6 Playerのインストール

ダウンロードにはかなり時間が掛かりますが、待っていれば終わります。これはNative Accessの良いところですね。とても楽です。

Boesendorfer 290の登録

最後にKontakt 6 PlayerにBoesendorfer 290がインストールされている位置を教える必要があります。CDからBoesendorfer 290をインストールした後に、下記のBrowseを押します。

スクリーンショットではBoesendorferがINSTALLED PRODUCTSのカテゴリに居ますが、初回の設定だとNOT INSTALLEDのカテゴリに居るはずです。1回インストールすると二度とNOT INSTALLEDに戻らないみたいです。これもNative Accessのイマイチなところ……。


Boesendorfer 290のインストール

このパス指定がまた曲者で、どのディレクトリを指定したら良いのかさっぱりわかりません。


Boesendorfer 290のインストールパス

正解はBoesendorferをインストールしたディレクトリの直下にある「Boesendorfer 290 Library」というディレクトリです。Boesendorfer_part1.nksやBoesendorfer_part2.nksというファイルが入っているはずです。

おそらく *.nksがKontakt用の音色データで、Kontakt Playerは音色データのあるディレクトリを知りたいんだろう、くらいの想像はつきますけど、正直に言ってわかりにくいです。

完成

設定がうまくいけばKontakt Playerの画面にBoesendorfer 290が出現するはずです。


Kontakt PlayerにBoesendorfer 290が出れば成功

無事、動作したので良かったですけど、初心者殺しのポイント(Preferences, *.nksのパス)が多くて疲れました……。

編集者:すずき(2020/06/21 14:49)

コメント一覧

  • ちまきさん(2021/09/27 03:16)
    初めまして、こんばんは!
    音源は異なりますが、自分も同じ悩みがあり検索した結果、本記事にたどり着きました。
    まさかKontakt Playerから読み込むとは目からうろこでした…(笑)
    もうだめかとあきらめた音源を再び使用することができてよかったです。
    貴重な情報をありがとうございました!
  • すずきさん(2021/09/27 17:05)
    お役に立てて何よりです。これ、わかりにくいですよね……。
  • 斎藤さん(2024/04/06 16:25)
    「Preferencesというメニューからディレクトリの設定ができます。パスを指定するだけではダメで、ディレクトリが存在していないとさっきのエラーが出ます。」ということでエラーの原因がなんとなくわかったのですがこの後どのようにすればうまくいくのかご教授頂けますと助かります。3週間ほどいろいろやっておりますがなかなかうまくいきません。
  • すずきさん(2024/04/06 22:54)
    ディレクトリを予め作成しておけば良いです。

    例えばc:\app\native_accessしかない状態で、c:\app\native_access\downloadと指定するとエラーになります。
    なのでエクスプローラか何かでc:\app\native_accessを表示し、その下にdownloadディレクトリを作成するとエラーが出なくなるはずです。
open/close この記事にコメントする



2020年6月20日

睡眠時無呼吸症候群とCPAP

半年くらい前からSAS(睡眠時無呼吸症候群)の対症療法としてCPAP(経鼻的持続陽圧呼吸器)を使っています。しかし、こいつがどうも息苦しくて合ってない気がしてなりません。

呼吸が止まるとSpO2(血中酸素飽和度)が下がります。医療機器以外でSpO2を測る手段はありませんから、先日、思い切ってメモリ機能付きのパルスオキシメーター(=SpO2計測器)Ubi-x LUKLA 2800mを購入しました。お値段6万円です。さすが医療機器、高いなー。


SpO2の変化、CPAPあり

添付のグラフはSpO2(青、左目盛り)と、脈拍(オレンジ、右目盛り)です。正常時のSpO2は98〜99%ですが、深夜4時3分に突如88%まで下がっています。

SpO2 88%がどれほど危険なのかわからなかったので、わざと息を止めてSpO2を下げる実験をしたところ「酸欠で目がチカチカして暗くなってきて、このまま死ぬんじゃねー?」と思うくらいでやっとSpO2 92%でした。88%とは一体……??

CPAPなし

CPAPなしだとどうなるかも測りました。CPAPありのときよりも頻繁にSpO2が低下しています。


SpO2の変化、CPAPなし

寝苦しくて辛いけどCPAPには意味があるということなんでしょうかねえ?

まだ1度ずつしか測っていないので、CPAPの効力については何とも言えません。今後CPAPあり・なし、仰向き、横向きなど条件を変えて何度か測ってみようと思います。

メモ: 技術系の話はFacebookから転記しておくことにした。大幅に追記。

編集者:すずき(2020/06/21 16:52)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月22日

LinuxデバイスツリーのYAMLドキュメントチェッカー

目次: Linux

最近、というほどでもないのですが、Linux KernelのデバイスツリーのドキュメントはYAMLで書く方が主流らしいです。

ドキュメントの書き方が良くわからず、合っているのか間違っているのかわからなくて困っていたんですが、ドキュメント Writing DeviceTree Bindings in json-schema を見ていたところ、YAMLチェッカーの存在を知りました。

DeviceTree Bindings (json-schema版) のチェック
$ make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/xxxx.yaml

こんな感じで使います。DT_SCHEMA_FILESを指定せずに実行すると全てのYAMLをチェックしようとしますが、16並列でもかなり時間が掛かりますから、自分が変更したファイルだけチェックしたほうが効率的でしょうね。

編集者:すずき(2024/02/27 02:26)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月26日

STATIONflow小技

目次: ゲーム

STATIONflowのしょうもない小技。

将来的に、どこに駅の入り口と電車の乗り場が出てくるか?を見たい場合は「マップ作成」で見たいマップを「複製」「マップ編集」すれば、全ての入り口、乗り場を見ることができます。


マップ作成メニュー画面

STATIONflowの面白さは、どこに増設されるかわからない入り口や乗り場に対応することではあるのですが、記録を狙ったりするときや、事前に完璧な駅建築計画を立てたい人は、覗いてみると役立つかもしれません。

編集者:すずき(2024/06/03 23:58)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月27日

STATIONflowプレイ日記

目次: ゲーム

STATIONflowの基本は理解したつもりなので、実績解除に挑んでますが、難しくて取れないものがいくつかあります。

今のところ一番難しいと思うのは「エレベスト」です。条件は「駅ランク20以上で階段とエスカレーターを設置せずにA+ 評価」です。

ありがちな条件ですが、STATIONflowのエレベーターはとてもヘボいため達成は困難です。

輸送力が低い
特に電車乗り場が顕著で8基設置しても客を捌ききれません。
輸送距離に応じて待ち時間が長くなる
長距離(例えばB1〜B5など)輸送すると、やたら待たされます。
客が待ち行列から離脱する
階段ユーザー(通勤客、学生)は非常に短気で、すぐに離脱します。

一番問題なのは3つ目の条件です。通常、待ち行列から離脱した客は階段やエスカレーターを利用しますが、移動手段がエレベーターしかない場合、不可解な動きをします。

  • エレベーターに並ぶ
  • エレベーターがこない
  • 待ち行列から外れる
  • 他の移動手段を探すが見つからない(※)
  • またエレベーターに並ぶ
  • エレベーターがこない
  • (以降、繰り返し)

(※)彷徨っている間はエレベーターが到着しても乗りません。一番訳が分からない動きです。しかもこの彷徨う時間が長い。

このようなおかしな行動をし続けた挙句、勝手に怒り始めて、駅の評価を下げてきます。理不尽極まりないですね。


エレベーターが来てるのに乗らない&怒ってる様子

ランク20到達後も駅と人が増え続けるため、どんどん客が捌ききれなくなり条件が悪くなる一方ですから、「エレベスト」実績を達成する最大のチャンスは「ランク20になる瞬間」でしょう。

が、私の腕では評価Aが限界でした。

もっと頑張ればできるのかもしれないですが、何回も同じマップをランク20までやり直すのは時間掛かって辛いし面白くないのでもうやりません……。

編集者:すずき(2024/06/03 23:59)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年6月28日

STATIONflowまさかの実績解除方法

目次: ゲーム

STATIONflowのしょうもない小技 その2です。

実績の解除が非常に難しい「エレベスト」や「効率の鬼」のような難しい実績が、超簡単に取れる方法です。

例えば、エレベストの条件は「駅ランク20以上で階段とエスカレーターを設置せずにA+ 評価」です。前回(2020年6月27日の日記参照)お伝えした通り、エレベーターと通勤客の変な挙動が合わさって、まともにやるとかなり難しいです。

しかしSTATIONflowの実績判定は甘々で「00:00になった瞬間」しか見ません。従って、

  • ランク20、A+ 評価の駅を作る(階段、エスカレーター使用可)
  • 23:50になったら時間を止める
  • 階段、エスカレーターなど、実績取得の妨げとなるものを全て破壊する
  • 時間を再始動させ00:00を迎える

これだけで達成できます。正直、こんな低レベルな小細工が通じると思わなかったので、逆にびっくりしました……。

類似した実績「階段抜き」「ノンエスカレーター」「エレベスト」「効率の鬼」ならば同じ手が通用するはずです。

作成者の想定とは違うだろうという意味で「邪道」な感じはしますが、バグを突いた挙動でもなさそうだし、早解きしたい方は利用してみても良いでしょう。

編集者:すずき(2024/06/03 23:59)

コメント一覧

  • 匿名さん(2020/07/11 18:26)
    「階段抜き」「ノンエスカレーター」「効率の鬼」を除いて通常プレイで実績解除したのですが、こんな方法があったとは・・・検索からたどり着いたのですが眼から鱗でした。ありがとうございました。
  • すずきさん(2020/07/12 00:53)
    コメントありがとうございます。私もやってみてびっくりしました。何でも試してみるものですね……。
open/close この記事にコメントする



2020年6月29日

ノートPCの発熱を抑える方法 - その1

目次: Windows

その2はこちら。

ノートPCでゲームをしていると、筐体が焼けそうなほど熱くなり、キーボードまで熱くなってキーを打つのが辛いです。

発熱の原因はグラフィックスチップですが、どうもCPUも無罪ではないらしく、TurboBoostを無効にするとややマシになることがわかりました。

私のマシンでは「電源オプション」 - 「プロセッサの電源管理」 - 「最大のプロセッサの状態」にて、87%以下にするとTurboBoostがOFFになりました。


最大のプロセッサの状態を87%に設定

下記は87%設定(1.49GHz)と88%設定(3.38GHz)にしたときのCPU動作周波数の変化です。


87%に設定したときのCPU動作周波数は1.49GHz


88%に設定したときのCPU動作周波数は3.38GHz

当然ながら1.49GHzと3.38GHzでは性能に天と地ほどの差があって、1.49GHzだとSTATIONflowの画面はめちゃくちゃカクつきます。

しかしシミュレーションゲームでは、待っているだけの時もありますし、常に爆熱で動いてくれる必要はありません。TurboBoostを任意にOFFにできるのは非常に便利です。

設定と動作周波数の関係一覧

CPUのクロック周波数の上限は何段階かあるようなので、変化点を調べました。

  • 〜60%: 0.99GHz
  • 〜65%: 1.10GHz
  • 〜71%: 1.19GHz
  • 〜76%: 1.30GHz
  • 〜82%: 1.39GHz
  • 〜87%: 1.49GHz
  • 〜100%: 3.36GHz

こんな感じでした。Core i5-8250Uはベース周波数1.6GHz、ブースト周波数3.4GHzなのにベース周波数である1.6GHzに張り付く設定は存在しません。謎です。

タスクマネージャーに「基本速度1.8GHz」と表示されているのも謎です。どこから1.8GHz出てきた……??

編集者:すずき(2023/09/24 12:41)

コメント一覧

  • hdkさん(2020/06/30 23:55)
    Athlon 5350 (2.05GHz) をUEFI Setupで上限1.1GHzに設定して使っているのですが、基本速度のところにはちゃんと1.10 GHzと出ています。1.80 GHzとは...
  • すずきさん(2020/07/01 11:18)
    うちのマシンは基本速度が取得できてないんですかねえ?BIOS は 1.6GHz とおっしゃっているんですが。
    仮に Windows が言ってる 1.8GHz が正解だとしても、1.8GHz に張り付く設定もないので、やっぱり謎は残ります。
  • s16223さん(2023/08/08 23:06)
    うちの8250Uもベースクロックが1.8GHzになってますね、、、 いい情報ありがとうございます。
  • すずきさん(2023/08/10 00:51)
    何の数字なのか結局よくわからなかったですが、ご参考になれば幸いです。
open/close この記事にコメントする



2020年6月30日

STATIONflowの駅の評価

目次: ゲーム

以前(2020年5月28日の日記参照)STATIONflowで速度3にすると駅の評価が下がることをお伝えしましたが、若干間違っていて「画面の処理落ちで評価が下がる」方が実態に近そうです。

速度2でA+ 評価の駅を使って実験したところ、ノートPCのクロック周波数を低くするだけで、駅の評価がAに下がりました。

駅の評価と見せかけて、実はマシンの評価も入ってるんでしょうか?余計なお世話なので、勘弁してくれよ。

編集者:すずき(2024/06/03 23:59)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報